[Edu-sig] Understanding [ [ ], [ ] ]
David MacQuigg
macquigg at ece.arizona.edu
Mon Apr 26 22:13:25 CEST 2010
Here are some challenges to test students' understanding of how Python
handles objects in memory. Scroll down one line at a time and see if
you can guess True or False on each equality or identity test.
### More on copying of complex objects
>>> from copy import copy, deepcopy # functions for shallow and deep
copies
>>> x
<__main__.X object at 0x14cab50>
>>> a = 2*[[x]]
>>> a
[[<__main__.X object at 0x14cab50>], [<__main__.X object at 0x14cab50>]]
>>> a[0] == a[1] # equal in value
True
>>> a[0] is a[1] # same object
True
>>> a = [[x] for i in range(2)] # trick to make distinct objects
>>> a = [copy([x]) for i in range(2)] # same, but more clear
>>> a
[[<__main__.X object at 0x14cab50>], [<__main__.X object at 0x14cab50>]]
>>> a[0] is a[1]
False
>>>
>>> b = a # new label for same object
>>> b is a
True
>>>
>>> b = a[:] # common Python idiom for shallow copy
>>> b = copy(a) # same, but more clear
>>> b
[[<__main__.X object at 0x14cab50>], [<__main__.X object at 0x14cab50>]]
>>> b == a
True
>>> b is a
False
>>> b[0] is a[0]
True
>>> b[0] is b[1]
False
>>> b = deepcopy(a) # clone everything
>>> b
[[<__main__.X object at 0x14d1d90>], [<__main__.X object at 0x14d1d90>]]
>>> b == a
# careful, object x has no "value"
# if an object has no value, it can't be == to anything but itself
False
>>> b[0] is a[0]
False
>>> b[0] is b[1]
False
>>> b[0][0]
<__main__.X object at 0x14d1d90>
>>> b[0][0] == a[0][0]
False
>>> b[0][0] == b[1][0]
True
--
************************************************************ *
* David MacQuigg, PhD email: macquigg at ece.arizona.edu * *
* Research Associate phone: USA 520-721-4583 * * *
* ECE Department, University of Arizona * * *
* 9320 East Mikelyn Lane * * *
* http://purl.net/macquigg Tucson, Arizona 85710 *
************************************************************ *
David MacQuigg wrote:
> Mark Engelberg wrote:
>> On Fri, Apr 23, 2010 at 2:41 PM, David MacQuigg wrote:
>>
>>> Would you rather have Python do something different?
>>
>> My own preference is that I would like 5*[[]] to be syntactic sugar for:
>> [ [ ] for i in range(5)]
>
> Then what about 5*[x], 5*[[x]], 5*[[3,x]], ... where x itself can be a
> list, a list of lists, or any complex object? How deep would you go
> in creating distinct objects? Would you want similar behavior for
> dictionaries, or is this just something special for lists? What about
> other operators? Is [x] + [x] no longer equivalent to 2*[x]? We need
> a clear, concise rule for when to generate distinct objects, and when
> to keep the current, and more efficient behavior - multiple references
> to the same object.
>
>> I find this to be more intuitive, and I believe this is what most
>> people incorrectly assume the syntax expands to.
>>
>
> The important thing is not that our initial guess is right in every
> odd case, but that the syntax be simple and consistent, so we get it
> right if we think about it. My initial guess was wrong, but that is
> OK with me, because 1) I could have figured it out if I had taken the
> time, and 2) I usually don't take the time to figure these things out.
> I just pop an example in the interpreter, and see if I get what I want.
>
> The seemingly bizarre behavior of a = 5*[[]] is not so bizarre if you
> really understand the relationship between variables and objects in
> Python. It's simple, but it's different than other languages. Our
> students start with C, so when I explain Python, I really emphasize
> the difference.
> http://ece.arizona.edu/~edatools/ece175/Lecture/python-variables.htm
>
>> If that's the way it worked, literals would always create fresh copies
>> each time through the loop, but if you still want shared behavior, you
>> could do it explicitly with something like:
>> x = []
>> a = 5*x
>>
>
> That changes the meaning of * for list objects from its current and
> very useful semantics of "extend this list", and five times empty is
> still empty.
>
>> Anyway, every language has its shares of gotchas.
>
> Agreed. I think of language design as laying a carpet in an
> odd-shaped room (the real world of problems we need to solve). A good
> design like Python will be very smooth in the middle, and the wrinkles
> will be at the edges. I've never needed to construct a list of lists,
> identical in value, but distinct objects in memory, so this is an
> edge-case for me.
>
> Bill's example of accidental scoping is a much more troublesome
> gotcha. Even if you are not pushing the edges, a simple mis-spelling
> coupled with Python's complex implicit scoping rules, can result in an
> error not detected by the interpreter or even by pychecker.
>
> If I were designing a language, external variables would always be
> explicitly declared, and the rule would be simple. An external
> variable refers to the nearest enclosing scope in which that variable
> is assigned a value. No difference if the enclosing scope is a class
> or another function or the module itself. No need for a 'global'
> keyword that actually means "module level", not truly global.
>
> -- Dave
>
More information about the Edu-sig
mailing list