On 01/06/2013 12:37 PM, alex23 wrote:
On Jan 6, 5:49 am, someone <newsbo...@gmail.com> wrote:
I thought that python also used "true" pass-by-reference, although I
haven't figured out exactly when I have this problem. I can just see
that sometimes I get this problem and then I need to copy the variable,
if I don't want the original data of the variable to be overwritten...
Generally I find it easier to call variables 'labels' or 'references';
you're not stashing a value into a slot, you're giving a name to an
object. So you're never really passing values around, just labels that
refer to an object.
Ok.
The confusion kicks in because there are two types of object: mutable
and immutable. Mutable objects can change, immutable objects cannot.
Yes, I've seen that described before.
Operations on an immutable object will return a _new_ immutable
object; the label used in an operation on an immutable object will
refer to the new object, any other references to the original object
will continue to refer to the original. Strings, numbers, tuples and
frozensets are all immutable built-in types.
>>> a = "alpha"
>>> b = a
>>> a = a + "bet"
>>> a
'alphabet'
>>> b
'alpha'
Ok, I think I knew some of these things, however I didn't think so much
about them before now.
With immutable types, you're safe to pass them into a function, act on
them, and not have references in the outer scope reflect the change:
>>> def double(x): return x * 2
...
>>> a = "alpha"
>>> double(a)
'alphaalpha'
>>> a
'alpha'
This is exactly what I've found out happens to me some times. Until now
I've managed to fix my problems. But it's good to remember this thing
with immutable vs. mutable types, which was something I didn't think
much about before. I'll think about this difference in the future, thank
you.
Everything else, including user defined objects, tend to be mutable:
>>> a = dict(foo=1,bar=2)
>>> b = a
>>> a['foo'] = 99
>>> a
{'foo': 99, 'bar': 2}
>>> b
{'foo': 99, 'bar': 2}
Yes, I've noticed this a lot of times in my own coding...
With mutable objects, you're _always_ operating on the _same_ object
that everything is referring to, even when you pass it into a
different scope:
>>> def toggle_foo( x ): x['foo'] = not x['foo']
...
>>> a = dict(foo=True)
>>> toggle_foo(a)
>>> a
{'foo': False}
Exactly, what I've also experienced a few times.
Note that the `toggle_foo` function doesn't return anything, nor is
the value of a re-assigned. The object that a refers to is passed into
`toggle_foo`, modified in place, and all references to it remain
pointing to the same, now modified object.
Yes, I notice that, thanks.
This is one of the big causes of unexpected behaviour to new Python
users, but as you get a better understanding of how Python's object
model works, you'll see it's really quite consistent and predictable.
It was a bit surprising to me in the beginning - though I'm still a
beginner (or maybe now almost an "intermediate" user), the good
explanation you come with now wasn't something I've thought so much of
before now. But I've clearly experienced many of the examples you write
about here, in my own coding and I've usually been very careful about
checking if my original object was modified un-intentionally. I think I
can deal with this now.
There are a couple of ways you can ensure you're always working with a
copy of an object if you need to. For lists, the canonical way is:
>>> a = [1,2,3]
>>> b = a
>>> a = [1, 2, 3]
>>> b = a[:] # shallow copy of a
>>> b.append(99)
>>> b
[1, 2, 3, 99]
>>> a
[1, 2, 3]
`b = a[:]` uses slice notation to create a new list that contains
everything in the original list, from start to end. This is called a
"shallow" copy; `a` and `b` both refer to _different_ lists, but the
objects within are the _same_ objects. For numbers, this isn't
Ok, good to know.
important, as they're immutable. For mutable objects, however, it's
something you need to bear in mind:
>>> a = [ [1,2], [3, 4] ] # list of lists
>>> b = a[:]
>>> b[0][0] = 99
>>> b
[[99, 2], [3, 4]]
>>> a
[[99, 2], [3, 4]]
So here, while `b` refers to copy of `a`, that copy contains the
_same_ list objects that `a` does, so any changes to the internal
lists will be reflected in both references, while any changes to `b`
itself won't be:
>>> b.append([5,6])
>>> b
[[99, 2], [3, 4], [5, 6]]
>>> a
[[99, 2], [3, 4]]
Yes, I've experienced this kind of thing before - but it's a very very
good explanation you give me know. It makes me understand the problem
much better, next time I experience it...
This is where the `copy` module comes to the rescue, providing a
shallow copy function `copy`, as well as `deepcopy` that makes copies
of all the objects within the object:
>>> import copy
>>> a = [ [1,2], [3, 4] ] # list of lists
>>> b = copy.deepcopy(a)
>>> b[0][0] = 99
>>> b
[[99, 2], [3, 4]]
>>> a
[[1, 2], [3, 4]]
If you plan on using the `copy` module, it's worthwhile readings it
docs, as there are some nuances to it that I'm glossing over here.
TBH, I don't recall every needing to use `copy` in my code.
I almost had to use this "copy.deepcopy" the other day, but I googled
for it and then I found a way to avoid it...
Hope this helps bring consistency where you currently find confusion :)
Yes, this is a VERY very good explanation. I was a bit confused about
when I created my own user defined objects, but now you explained that
these tend to be mutable which is also my experience.
I'll still keep an extra eye out for this special way of sending objects
back and forward between function, in order to un-intentionally
overwrite some data...
Thanks you very much for a very good and precise description of this
phenomena of the python-language (or what I should call it) :-)
--
http://mail.python.org/mailman/listinfo/python-list