Can global variable be passed into Python function?

M

Marko Rauhamaa

Steven D'Aprano said:
But your code doesn't succeed at doing what it sets out to do. If you try
to call it like this:

py> x = 23
py> y = 42
py> swap(x, y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in swap
AttributeError: 'int' object has no attribute 'get'

not only doesn't it swap the two variables, but it raises an exception.
Far from being a universal swap, it's merely an obfuscated function to
swap a few hard-coded local variables.

You are calling the function wrong. Imagine the function in C. There,
you'd have to do this:

x = 23;
y = 42;
swap(&x, &y);

You've left out the ampersands and gotten a "segmentation fault."

You should have done this:

x = 23
y = 42

class XP:
def get(self):
return x
def set(self, value):
nonlocal x
x = value

class YP:
def get(self):
return y
def set(self, value):
nonlocal y
y = value

swap(XP(), YP())


So we can see that Python, too, can emulate the ampersand, albeit with
some effort.


Marko
 
C

Chris Angelico

Now I daresay that under the hood, Pascal is passing the address of foo
(or bar) to the procedure plus, but inside plus you don't see that
address as the value of b. You see the value of foo (or bar).

C does not do that -- you have to manually manage the pointers yourself,
while Pascal does it for you. And Python also has nothing like that.

Yep. I should have clarified that I wasn't talking about Pascal; I'm
not fluent in the language (last time I did anything at all with
Pascal was probably about ten years ago, and not much then). In C, it
strictly does what I said: & takes the address of something, *
dereferences an address. There's no way to "pass a variable" - you
have to pass the address, and that has consequences if, for instance,
you *return* an address and the variable ceases to exist. (Does Pascal
have an equivalent of that?)

And Python has no such concept, anywhere. But anything that you can
achieve in C using pointers, you can probably achieve in Python using
more complex objects.

ChrisA
 
W

wxjmfauth

# a swapping variant.... ab = [a, b]
.... ab[1], ab[0] = ab[0], ab[1]
.... return ab[0], ab[1]
....(111, 505627864)

jmf
 
C

Chris Angelico

... ab = [a, b]
... ab[1], ab[0] = ab[0], ab[1]
... return ab[0], ab[1]

Provably identical to:

def swap(a, b):
return b, a

The rest is just fluff.

ChrisA
 
S

Steven D'Aprano

Yep. I should have clarified that I wasn't talking about Pascal; I'm not
fluent in the language (last time I did anything at all with Pascal was
probably about ten years ago, and not much then). In C, it strictly does
what I said: & takes the address of something, * dereferences an
address. There's no way to "pass a variable" - you have to pass the
address, and that has consequences if, for instance, you *return* an
address and the variable ceases to exist. (Does Pascal have an
equivalent of that?)

Yes, Pascal has pointers as a first-class data type. Syntax is similar to
C, ^x is a pointer to x, p^ dereferences the pointer p.

And Python has no such concept, anywhere. But anything that you can
achieve in C using pointers, you can probably achieve in Python using
more complex objects.

Not even that complex. Although Python doesn't do pointers, the model of
the language is such that you don't need to. Where in C or Pascal you
would pass a pointer to a record, in Python you just pass the record,
safe in the knowledge that the entire record won't be copied.

There are a few idioms which don't work as neatly in Python as in Pascal,
such as output parameter, but you don't need them. Just return a tuple.
If you insist on an output parameter, do it like this:


def func(inarg, outarg):
if inarg % 2:
outarg[0] == "even"
else:
outarg[0] == "odd"
return inarg + 1

out = [None]
x = 42

result = func(x, out)
print(out[0])
 
S

Steven D'Aprano

You are calling the function wrong. Imagine the function in C. There,
you'd have to do this:
[...]


Sorry, I misunderstood you. When you called it a universal swap function,
I thought you meant a universal swap function. I didn't realise you
intended it as a demonstration of how to emulate a C idiom using overly-
complicated Python code *wink*

If you want to emulate pointers in Python, the simplest way is to use
lists as pseudo-pointers.

# think of ptr[0] as pointer dereferencing
# think of [value] as quasi "address of" operator
def swap(p, q):
p[0], q[0] = q[0], p[0]

x = ["anything"]
y = ["something"]
z = [23]

swap(x, y)
swap(x, z)

print(x[0], y[0], z[0])
=> prints "23 anything something"


But why bother to write C in Python? Python makes a really bad C, and C
makes a really bad Python.
 
C

Chris Angelico

Yes, Pascal has pointers as a first-class data type. Syntax is similar to
C, ^x is a pointer to x, p^ dereferences the pointer p.

Right, I remember those now. Yes. (See how rusty I am on it? Heh.)

ChrisA
 
M

Mark Lawrence

BASIC, C, FORTRAN, COBOL, Assembly... A "variable" is synonym for an
address [a box that holds things].

In C.

int xyz = 1;

xyz is placed in a register. What is xyz called now as it's not in memory?
 
M

Marko Rauhamaa

Mark Lawrence said:
BASIC, C, FORTRAN, COBOL, Assembly... A "variable" is synonym
for an address [a box that holds things].

In C.

int xyz = 1;

xyz is placed in a register. What is xyz called now as it's not in
memory?

It's still a box, just like in Python.

The difference is that while in C, the box looks like this:

<URL: http://www.daikudojo.org/Archive/daikusan/bob.le/20090314_dovetail_box/pics/DSC_0767.JPG>

in Python, it looks like this:

<URL: http://www.dejavubuffet.fi/wp-content/uploads/2012/10/korurasia31.jpg>


Marko
 
D

Dave Angel

Mark Lawrence said:
BASIC, C, FORTRAN, COBOL, Assembly... A "variable" is synonym for an
address [a box that holds things].

In C.

int xyz = 1;

xyz is placed in a register. What is xyz called now as it's not in memory?

Don't know why you'd assume it's a register. It could just as
well be nowhere. If a later reference in the same function adds
it to something else, there might not need to be any hardware
anywhere representing the value 1.

Once you turn on a C optimizer, the real existence of local values
is not assured.
 
S

Steven D'Aprano

As I recall, to handle garbage collection, Apple used to use two stage
look ups... The user variable (handle) was a reference into a table of
handles, and each entry in that table was a reference to the real object
out in memory. Garbage collection would move the objects around to
compact used memory, updating the address in the table -- the user
program never sees the object moving as its handle address never
changed.

Yes, but that was not transparent to the user. You actually had to
explicitly use the Mac Toolbox memory routines to allocate memory, create
and destroy handles, etc.

If you just used your programming language's normal pointers, they
couldn't and wouldn't move.
 
S

Steven D'Aprano

BASIC, C, FORTRAN, COBOL, Assembly... A "variable" is synonym for
an address [a box that holds things].
In C.

int xyz = 1;

xyz is placed in a register. What is xyz called now as it's not in
memory?

Of course it is in memory, just not main memory, and it is still accessed
via an address. It's just that the address is something equivalent to
"Register 5" instead of "address 12345678 in RAM".

You're focusing on the wrong thing here. The distinction is not "in main
memory" versus "in a register" (or somewhere else). The distinction is
not *where* the value lives, but the semantics of what it means to
associate a name with a value.

In C or Pascal-style languages, what we might call the "fixed address"
style of variables, a variable assignment like xyz = 1 does something
like this:

- associate the name 'xyz' with some fixed location
- stuff the value 1 into that location


In Python-style languages, what we might call the "name binding" style of
variables, that same xyz = 1 means:

- find or create the object 1
- associate the name 'xyz' with that object


In implementations like Jython and IronPython, the object is even free to
move in memory while in use. But that's not the only difference. The big
difference is that in "fixed location" languages, it makes sense to talk
about the address of a *variable*. In C, you might ask for &xyz and get
123456 regardless of whether xyz is assigned the value 1, or 23, or 999.
But in Python, you can't ask for the address of a variable, only of the
address of an *object* (and even that is useless to you, as you can't do
anything with that address).
 
C

Chris Angelico

In C or Pascal-style languages, what we might call the "fixed address"
style of variables, a variable assignment like xyz = 1 does something
like this:

- associate the name 'xyz' with some fixed location
- stuff the value 1 into that location

Kinda. In its purest sense, C is like that. When you declare "int
xyz;", the compiler allocates one machine word of space either in the
data segment (if that's at top level, or is declared static) or on the
stack (if it's local), and records that the name xyz points there. But
an optimizing C compiler is allowed to do what it likes, as long as it
maintains that name binding... and as long as any recorded address of
it remains valid. It's actually very similar to what was discussed in
another thread recently about PyPy and the id() function - the
compiler's free to have xyz exist in different places, or not exist at
all, as long as the program can't tell the difference. I don't know
whether PyPy allocates an id for everything or only when you call
id(), but if the latter, then it's exactly the same as a C compiler
with the address-of operator - if you never take the address, it
doesn't have to have one (and even if you do, it's free to fiddle with
things, unless you declare the variable volatile).

So, these days, C is becoming more like Python.

ChrisA
 
S

Steven D'Aprano

Kinda. In its purest sense, C is like that. When you declare "int xyz;",
the compiler allocates one machine word of space either in the data
segment (if that's at top level, or is declared static) or on the stack
(if it's local), and records that the name xyz points there. But an
optimizing C compiler is allowed to do what it likes, as long as it
maintains that name binding... and as long as any recorded address of it
remains valid.


I think that's a red herring. The semantics of the language are that it
behaves as if the variable had a fixed location. What goes on behind the
scenes is interesting but fundamentally irrelevant if you want to
understand the language itself: the compiler's implementation may give
that variable a fixed location, or not, or multiple locations, or non-
fixed, or whatever it finds useful at the time, so long as the C code you
write can assume that it is fixed. (That's like Python, which has no
pointers. The fact that the implementation of some Python interpreters
uses pointers all over the place is strictly irrelevant.)

In practice, because C compilers usually work so close to the metal, and
programmers expect there to be a very direct correspondence between the C
code you write and the machine code you get, the compiler is unlikely to
simulate fixed addresses unless there is real benefit to be gained, it is
more likely to actually use fixed addresses. But in principle, one might
write a C interpreter in Jython, and simulate fixed addresses over the
top of a language without any addresses at all (Python), which in turn is
written in a language where the garbage collector can move things around
(Java).

The important thing here is not so much that we are disagreeing, but that
we are talking about two different levels of explanation. ("Gödel, Escher
And Bach" has an very interesting section about how explanations at
different levels can be radically different and even contradict each
other.) At the C source code level, the language mandates that variables
have fixed addresses, to the extent that C source code knows about
addresses at all. At the generated machine code level, the compiler is
free to play tricks if necessary.

Another example: if you have a variable unused=23, and the compiler
removes it because it's dead code, we wouldn't argue that therefore C
variables have no existence at all. Would we? :)

So, these days, C is becoming more like Python.

*raises eyebrow*

I think the stress of the exception-expression PEP is getting to you.
They truly aren't.

*wink*
 
C

Chris Angelico

The important thing here is not so much that we are disagreeing, but that
we are talking about two different levels of explanation. ("Gödel, Escher
And Bach" has an very interesting section about how explanations at
different levels can be radically different and even contradict each
other.) At the C source code level, the language mandates that variables
have fixed addresses, to the extent that C source code knows about
addresses at all. At the generated machine code level, the compiler is
free to play tricks if necessary.

I think that's a good summary, actually. C has variables, at the
source code level, but once you compile it down, it might not. From
which it follows that C variables have addresses (unless they're
declared 'register', in which case they might still be stored in RAM
but no longer have addresses), which may or may not have any actual
relationship to memory addresses.
Another example: if you have a variable unused=23, and the compiler
removes it because it's dead code, we wouldn't argue that therefore C
variables have no existence at all. Would we? :)


*raises eyebrow*

I think the stress of the exception-expression PEP is getting to you.
They truly aren't.

*wink*

Haha. I wasn't joking, though; a fully-optimizing compiler is free to
do so much that, in reality, you're not writing bare-metal code -
you're writing guidelines to a sophisticated program generator. C has
become as high level a language as any other, and has acquired some
Python-like features (check out some of the recent standards); and,
the part that I was talking about here, it makes the most sense to
talk about "name bindings" rather than memory addresses. Variables
might move around, but if you say "int xyz=5;", then you can be sure
that querying xyz will give you back 5. The concreteness of "declare a
variable ergo memory is allocated for it" is no longer the case.

ChrisA
 
M

Marko Rauhamaa

Steven D'Aprano said:
The big difference is that in "fixed location" languages, it makes
sense to talk about the address of a *variable*.

The address could be a symbol, too.

The Python statement

xyz = 3

places a number in the address "xyz".

You can read the value from the address "xyz" with

locals()["xyz"]


Marko
 
S

Steven D'Aprano

The address could be a symbol, too.

The Python statement

xyz = 3

places a number in the address "xyz".

It doesn't matter whether addresses are numeric or symbolic, Python does
not use that model for variables. Consider this example. There are a few
steps, so let's go through them. First we prove that so-called address
"abc" and "xyz" are distinct. If they were the same address, then they
would logically have to contain the same value, but that is not the case:

abc = 23
xyz = 42
assert abc != xyz


This proves that the two addresses are different.

Now we prove that they are the same address:

abc = xyz = []
xyz.append(42)
assert abc == [42]


If they were different, then modifying the object at xyz cannot modify
the object at abc. So we have a contradiction: "addresses" abc and xyz
are both the same address, and different addresses.

There is a way to wiggle out of the contradiction: accept that the
addresses are distinct, but claim that a single object can be in two
different locations at once. But if we make that argument, we are
stretching the metaphor of location quite considerably. Absent time-
travel, objects cannot be in two locations at once. Claiming that they
can be requires stretching the metaphor of location past breaking point.

Self-confession time: some years ago, I used to make exactly that
argument. I used to argue that the right way to visualise Python's
variable model was to consider that objects were stored in variables in
exactly the way you are suggesting. I didn't describe them as symbolic
addresses, but otherwise the model was the same. In order to make the
model work, I argued that objects could be in more than one place at
once, including *inside* itself:

L = []
L.append(L)

and explicitly argued that this didn't matter. After all, if I can watch
Doctor Who and accept the concept of his TARDIS materialising inside
itself, I can understand the idea of a list being inside itself.

That was before I really grasped the difference between the name binding
and fixed location variable models. While I'm still fond of the concept
of a box being inside itself, I've come to understand that having to
stretch the metaphor of location so far simply indicates that the
metaphor does not work with Python's semantics.

Python's actual behaviour is not a good fit for the "variables are
locations" model, not even if you think of locations in the abstract with
symbolic names rather the numeric addresses.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,079
Messages
2,570,574
Members
47,206
Latest member
Zenden

Latest Threads

Top