Return value of an assignment statement?
Bruno Desthuilliers
bruno.42.desthuilliers at wtf.websiteburo.oops.com
Fri Feb 22 04:45:26 EST 2008
Jeff Schwab a écrit :
> bruno.desthuilliers at gmail.com wrote:
(snip)
>> Explicitely using list.extend would make things clearer:
>>
>> def invoke_some_fct(parent):
>> parent.x.extend(['world'])
>
> Whether you use += or extend has nothing to do with it.
Mmm... Really ?
> You omitted the
> relevant part. Using extend, it would look like:
>
> y = parent.x
> y.extend(['world'])
>
> The confusing part is that performing an operation on y may or may not
> alter parent.x, depending on whether the initial type of parent.x is
> immutable.
given that Python doesn't copy anything unless explicitelly asked for, the
y = parent.x
statement has the very clear semantic of making y an alias of parent.x.
Mutability has nothing to do with it, except for the fact that if
parent.x is immutable there's indeed no way to mutate it.
> If parent.x is immutable, y is a copy of the value
> represented by parent.x,
No, by no way.
>>> class Parent(object): pass
...
>>> p = Parent()
>>> p.x = "aaa + bbb " # a string that won't be interned
>>> y = p.x
>>> y is p.x
True
y is *not* "a copy of x", it is another name bound to the very same object.
> and modifying y has not effect on the value of
> parent.x.
Your problem is with the semantic of "modifying". In Python, (re)binding
a name and mutating an object are two very distinct things.
If (the object referenced by) y is immutable, you *can not* modify (=>
mutate) it. Period.
And if you *rebind* y, this *won't* affect p.x, whether it's mutable or not:
>>> p = Parent()
>>> p.x = ['allo']
>>> y = p.x
>>> y is p.x
True
>>> y = ['la terre']
>>> y
['la terre']
>>> p.x
['allo']
>>> y is p.x
False
>>>
IOW - and while, you're right about this, I skipped the part that
trouble you, that is the aliasing of parent.x -, the observation that
using list.extend (that is, clearly a mutator method call) instead of
augmented assignment which *looks like* it's rebinding y (and would
effectively rebind it if y was immutable).
FWIW, it's IMHO a real wart - given Python's pretention at readability -
that augmented assignement has been implemented that way for lists.
> If (OTOH) parent.x is mutable, then x and y are really
> references to the same object, and modifications to that object via y
> can be observed via x. In C, you use pointers to get this effect.
Not quite the same thing. C variables are boxes (containing values),
with pointer's values being the coords of another box, while Python's
'variables' are only labels on objects - they *never* 'contains'
anything. (FWIW, someone recently posted a link to a very good
explanation of this, but I don't have it under hand right now - could
someone help ?)
With pointers, a function can modify the content of a box defined in the
caller's scope. This is not something you can do in Python - that is,
rebinding a formal parameter to a different object withing a function
won't affect the bindings in the caller's scope:
def rebinder(x):
print "before: x = %s (id: %s)" % (x, id(x))
x = ['bar']
print "after: x = %s (id: %s)" % (x, id(x))
def caller():
a = ['foo']
print "before: a = %s (id: %s)" % (a, id(a))
rebinder(a)
print "after: a = %s (id: %s)" % (a, id(a))
caller()
>
>> Now there's no reason to feel nervous about this. All you have to
>> remember is that Python never copy anything unless explicitely asked
>> for.
>
> It's not that simple. After a statement like:
>
> a = b
>
> Whether a and b denote the same object depends on what kind of object b
> represented in the first place.
No. You can bet your life on this : after this statement, a and b are
two labels for the very same object, *always*, *whatever* the type of b.
Now this is a very frequent cause of confusion for C/C++ programmers,
and it has been explained again and again here - usually far better than
I just did, so you may want to google for this.
HTH
More information about the Python-list
mailing list