[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