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