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

_______________________________________________
Edu-sig mailing list
Edu-sig@python.org
http://mail.python.org/mailman/listinfo/edu-sig

Reply via email to