Mutability of function arguments?

E

ex_ottoyuhr

I'm trying to create a function that can take arguments, say, foo and
bar, and modify the original copies of foo and bar as well as its local
versions -- the equivalent of C++ funct(&foo, &bar).

I've looked around on this newsgroup and elsewhere, and I gather that
this is a very common concern in Python, but one which is ordinarily
answered with "No, you can't. Neat, huh?" A few websites, newsgroup
posts, etc. have recommended that one ask for a more "Pythonic" way of
doing things; so, is there one, or at least one that doesn't involve
using objects as wrappers for mutable arguments?

And, indeed, would that approach work? Would declaring:

class FooWrapper :
__init__(fooToLoad) :
self.foo = fooToLoad

mean that I could now declare a FooWrapper holding a foo, pass the
FooWrapper to a function, and have the function conclude with the foo
within the FooWrapper now modified?

Thanks in advance for everyone's time; I hope I'm comprehensible.
 
C

Carl J. Van Arsdall

And, indeed, would that approach work? Would declaring:

class FooWrapper :
__init__(fooToLoad) :
self.foo = fooToLoad

mean that I could now declare a FooWrapper holding a foo, pass the
FooWrapper to a function, and have the function conclude with the foo
within the FooWrapper now modified?
Yes, passing FooWrapper will pass by reference to a python function
thereby allowing you to edit the fooToLoad

HTH,

carl


--

Carl J. Van Arsdall
(e-mail address removed)
Build and Release
MontaVista Software
 
F

Fredrik Lundh

ex_ottoyuhr said:
I've looked around on this newsgroup and elsewhere, and I gather that
this is a very common concern in Python, but one which is ordinarily
answered with "No, you can't. Neat, huh?" A few websites, newsgroup
posts, etc. have recommended that one ask for a more "Pythonic" way of
doing things; so, is there one, or at least one that doesn't involve
using objects as wrappers for mutable arguments?

anyone that holds a reference to a mutable object can modify it, and
everyone that has the same reference will see the changes. assignment
copies references, not values:
>>> foo = []
>>> value = foo
>>> foo.append("hello")
>>> foo.append("world")
>>> value
['hello', 'world']
And, indeed, would that approach work? Would declaring:

class FooWrapper :
__init__(fooToLoad) :
self.foo = fooToLoad

mean that I could now declare a FooWrapper holding a foo, pass the
FooWrapper to a function, and have the function conclude with the foo
within the FooWrapper now modified?

if self.foo is a mutable object, and your function is modifying it in
place, yes:
... def __init__(self, fooToLoad):
... self.foo = fooToLoad
...
>>> value = []
>>> foo = FooWrapper(value)
>>>
>>> foo.foo []
>>> value []
>>> def mutator(x):
... x.foo.append("hello")
...
>>> mutator(foo)
>>> mutator(foo)
>>>
>>> foo.foo ['hello', 'hello']
>>> value
['hello', 'hello']

however, if the mutator replaces the wrapped object, the original object
will not see the changes. you can still see them via the wrapper, of course:
... x.foo = ["goodbye"]
...
>>> mutator(foo)
>>> foo.foo ['goodbye']
>>> value
['hello', 'hello']

this might help:

http://effbot.org/zone/python-objects.htm

</F>
 
E

ex_ottoyuhr

(Re. mutability question:)

Update, never mind. I found that the FooWrapper solution isn't so bad
after all -- and even better is putting the variable in question in a
different module entirely.

However, anyone who wants to answer the question is still welcome to.
Sorry to be a bother, and to have posted before I thought... :)
 
B

Brett g Porter

ex_ottoyuhr said:
I'm trying to create a function that can take arguments, say, foo and
bar, and modify the original copies of foo and bar as well as its local
versions -- the equivalent of C++ funct(&foo, &bar).

I've looked around on this newsgroup and elsewhere, and I gather that
this is a very common concern in Python, but one which is ordinarily
answered with "No, you can't. Neat, huh?" A few websites, newsgroup
posts, etc. have recommended that one ask for a more "Pythonic" way of
doing things; so, is there one, or at least one that doesn't involve
using objects as wrappers for mutable arguments?

And, indeed, would that approach work? Would declaring:

class FooWrapper :
__init__(fooToLoad) :
self.foo = fooToLoad

mean that I could now declare a FooWrapper holding a foo, pass the
FooWrapper to a function, and have the function conclude with the foo
within the FooWrapper now modified?

Well, you can test it yourself:
.... def __init__(self, val):
.... self.val = val
........ w.val = 11
....
Thanks in advance for everyone's time; I hope I'm comprehensible.

You're comprehensible, but I think that you're also thinking in C++.
The object model that Python follows is very different -- instead of
thinking of assignment meaning
"Stick this value into this named location", you need to switch to
thinking of assignment as meaning "stick this name onto that object
until I tell you otherwise".

If you're trying to return multiple values from a function, Python lets
you do that
.... return x*2, y*2, z*2
....
 
M

Mark Tolonen

ex_ottoyuhr said:
I'm trying to create a function that can take arguments, say, foo and
bar, and modify the original copies of foo and bar as well as its local
versions -- the equivalent of C++ funct(&foo, &bar).

I've looked around on this newsgroup and elsewhere, and I gather that
this is a very common concern in Python, but one which is ordinarily
answered with "No, you can't. Neat, huh?" A few websites, newsgroup
posts, etc. have recommended that one ask for a more "Pythonic" way of
doing things; so, is there one, or at least one that doesn't involve
using objects as wrappers for mutable arguments?

And, indeed, would that approach work? Would declaring:

class FooWrapper :
__init__(fooToLoad) :
self.foo = fooToLoad

mean that I could now declare a FooWrapper holding a foo, pass the
FooWrapper to a function, and have the function conclude with the foo
within the FooWrapper now modified?

Thanks in advance for everyone's time; I hope I'm comprehensible.

Python isn't C++ and there is no need to return multiple values by modifying
function parameters:
.... return a+1,b+1
....
-Mark
 
M

Mike Meyer

ex_ottoyuhr said:
I'm trying to create a function that can take arguments, say, foo and
bar, and modify the original copies of foo and bar as well as its local
versions -- the equivalent of C++ funct(&foo, &bar).

C++'s '&' causes an argument to be passed by reference. Python does
that with all arguments. Any changes you make to the argument in the
function will be seen in the caller unless you explicitly make a copy
to pass.
I've looked around on this newsgroup and elsewhere, and I gather that
this is a very common concern in Python, but one which is ordinarily
answered with "No, you can't. Neat, huh?" A few websites, newsgroup
posts, etc. have recommended that one ask for a more "Pythonic" way of
doing things; so, is there one, or at least one that doesn't involve
using objects as wrappers for mutable arguments?

If your arguments are mutable, you don't need to do anything to be
able to change them - just call the mutator methods. If your arguments
aren't mutable, then you can't change them, either in the function or
in the original namespace.

If what you really want to do is rebind a variable in the calling
namespace - well, you can't do that. The standard way to deal with the
usual uses for passing references in C is to return the value (or
values - Python handles multi-valued return and assignment much
cleaner than C++) and rebind the variables at the point of the call.

If you insist on writing C/C++ in Python, you can wrap an immutable
object in an instance of class with a method to let you change it, as
you suggest:
And, indeed, would that approach work? Would declaring:
class FooWrapper :
__init__(fooToLoad) :
self.foo = fooToLoad
mean that I could now declare a FooWrapper holding a foo, pass the
FooWrapper to a function, and have the function conclude with the foo
within the FooWrapper now modified?

If you like an extended lambda calculus syntax, you can use my Ref
class: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/456150.

<mike
 
B

bonono

Mike said:
C++'s '&' causes an argument to be passed by reference. Python does
that with all arguments. Any changes you make to the argument in the
function will be seen in the caller unless you explicitly make a copy
to pass.
except when foo and bar are bound to immutable objects.

In C:

int foo=1;
int bar=2;

void update(int *a, int *b) { *a=3; *b=4}

update(&foo, &bar);

In Python:

foo=1
bar=2

def update(a,b): a=3; b=4

update(foo,bar)

Many people from C/C++ background would be tricked for this situation.
 
M

Mike Meyer

except when foo and bar are bound to immutable objects.
Wrong.

In C:

int foo=1;
int bar=2;

void update(int *a, int *b) { *a=3; *b=4}

update(&foo, &bar);

Note that this update is using an assignment statement, and thus
changing the arguments.
In Python:

foo=1
bar=2

def update(a,b): a=3; b=4

update(foo,bar)

This update isn't changing the objects, it's rebinding the names in
the local name space. Since you didn't change the objects, there's no
change to see in the calling environment.
Many people from C/C++ background would be tricked for this situation.

That's because they don't understand binding. Any language that has
bindings instead of has assignments will "trick" them this way.

<mike
 
A

Alex Martelli

Mike Meyer said:
That's because they don't understand binding. Any language that has
bindings instead of has assignments will "trick" them this way.

....Java being probably the most popular example...


Alex
 
B

bonono

Mike said:
Note that this update is using an assignment statement, and thus
changing the arguments.

void update(int a, int b) { a=3; b=4}

Is this also an assignment statement ?
This update isn't changing the objects, it's rebinding the names in
the local name space. Since you didn't change the objects, there's no
change to see in the calling environment.


That's because they don't understand binding. Any language that has
bindings instead of has assignments will "trick" them this way.
Sure, any language that behave like this would trick them, I am not
saying python is the only one or that there is anything wrong with this
behaviour. I was just saying that it is a situation that they get
tricked, because of their "perception" about "=", especially when they
are told that any change to the arguments are seen by the caller.
 
M

Mike Meyer

void update(int a, int b) { a=3; b=4}
Is this also an assignment statement ?

Yes. But C calls by value, and passes copies of the original a and b
in. So in this case, you change the copies.
Sure, any language that behave like this would trick them, I am not
saying python is the only one or that there is anything wrong with this
behaviour. I was just saying that it is a situation that they get
tricked, because of their "perception" about "=", especially when they
are told that any change to the arguments are seen by the caller.

Except "trick" is a poor word choice. Nobody is playing a trick on
them - they just don't understand what is going on.

<mike
 
B

bonono

Mike said:
Except "trick" is a poor word choice. Nobody is playing a trick on
them - they just don't understand what is going on.
oops, never thought about the negative meaning of it, it is just meant
as "not behave as expected", what would be the word you use then ?
 
M

Mike Meyer

oops, never thought about the negative meaning of it, it is just meant
as "not behave as expected", what would be the word you use then ?

Surprise?

<mike
 
B

bruno at modulix

ex_ottoyuhr said:
I'm trying to create a function that can take arguments, say, foo and
bar, and modify the original copies of foo and bar as well as its local
versions -- the equivalent of C++ funct(&foo, &bar).

This is already what you have. In Python, all you have are references to
objects, there is no "local version".
I've looked around on this newsgroup and elsewhere, and I gather that
this is a very common concern in Python, but one which is ordinarily
answered with "No, you can't. Neat, huh?"

Pardon ???
.... aList.append(aValue)
....
mylist = range(10)
mylist [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
appendToList(mylist, 42)
mylist [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 42]

Now the usual considerations apply :
- rebinding an arg has of course only local effect
- immutable objects are still immutables

Also note that since
1/ Python as a good support for exception handling
2/ a function can return multiple values [1],
there is less need for such constructs than in C or C++.

[1] the truth is that the function can return a unique tuple, that can
be unpacked as any other.
 
K

Kent Johnson

Mike said:
C++'s '&' causes an argument to be passed by reference. Python does
that with all arguments. Any changes you make to the argument in the
function will be seen in the caller unless you explicitly make a copy
to pass.

I would say, from the point of view of a C++ programmer, Python passes
references by value. In other words if you think of variables as
pointers (references) to values, and function call as passing the
reference by value, the behaviour of Python makes sense.

Kent
 
M

Mike Meyer

Kent Johnson said:
I would say, from the point of view of a C++ programmer, Python passes
references by value. In other words if you think of variables as
pointers (references) to values, and function call as passing the
reference by value, the behaviour of Python makes sense.

While the description is right, the terminology is wrong, and places
the emphasis in the wrong place.

Your description of "passes references by value" is a description of
call by reference. C passes all arguments by value, to pass a
reference, the C programmer creates the reference to the value "by
hand", then dereferences it by hand at the other end. So C's
"call-by-reference" passes the reference by value. There's no
difference between C's call-by-reference and Python's
call-by-reference, and using different words to try and imply there is
will just cause problems further on.

The real difference is in the way names behave in the two
languages. As you put it, "variables are references to values", except
Python names don't have most of the things associated with variables
in other programming languages, so it's better to call them names. We
use "bound" to show that we're not copying a value over a fixed memory
location, hence "names are bound to values." This is the crucial
point, and the one that need to be emphasized.

<mike
 
F

Fredrik Lundh

Mike said:
Your description of "passes references by value" is a description of
call by reference. C passes all arguments by value, to pass a
reference, the C programmer creates the reference to the value "by
hand", then dereferences it by hand at the other end. So C's
"call-by-reference" passes the reference by value. There's no
difference between C's call-by-reference and Python's
call-by-reference, and using different words to try and imply there is
will just cause problems further on.

can you guys please stop using "call by value" and "call by reference"
when you discuss Python. both terms have established meanings, and
Python's argument passing model doesn't match any of them.

this was known some 30 years ago; here's a quote from a CLU reference
manaual from 1979:

"We call the argument passing technique _call by sharing_,
because the argument objects are shared between the
caller and the called routine. This technique does not
correspond to most traditional argument passing techniques
(it is similar to argument passing in LISP). In particular IT
IS NOT call by value because mutations of arguments per-
formed by the called routine will be visible to the caller.
And IT IS NOT call by reference because access is not given
to the variables of the caller, but merely to certain objects."

(CLU was one of the first languages to use objects in the Python sense,
as well as the same argument passing model as today's Python)

established terms for Python's argument passing model are

call by object

or

call by sharing

for more on this, see the comp.lang.python archives.

</F>
 

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
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top