# "s" is a string containing a Python statement. "objstrlist" is a list of
# strings where each string is a python expression. "glo" and "loc" are used
# to set the namespaces. They default to the name spaces "watch" is called
# in. An example is "watch('x = 2 * [[]]', ['x[0]', 'x[1]']".
def watch(s, objstrlist, glo=globals(), loc=locals()):
exec s in glo, loc
print 'After executing "%s"' % s
for objstr in objstrlist:
v = eval(objstr)
t = str(type(v))[7:-2]
print '%10s : value %10s, id %9i, type %-10s' % \
(objstr, v, id(v), t)
# Executes the statement s, then prints information about all the local
# variables that were created.
def watchall(s, glo=globals(), loc=locals()):
# Make a copy of the locals.
old_locals = {}
for k, v in loc.items():
old_locals[k] = v
exec s in globals(), loc
print 'After executing'
print s
for objstr in loc.keys():
# Print only changed locals.
if objstr in old_locals.keys() and old_locals[objstr] == loc[objstr]:
continue
v = loc[objstr]
t = str(type(v))[7:-2]
print '%10s : value %10s, id %9i, type %-10s' % \
(objstr, v, id(v), t)
# Same as watchall but prints information about elements of lists.
def watchall1(s, glo=globals(), loc=locals()):
# Copy locals.
old_locals = {}
for k, v in loc.items():
old_locals[k] = v
exec s in globals(), loc
print 'After executing'
print s
for objstr in loc.keys():
# Print only changed locals.
if objstr in old_locals.keys() and old_locals[objstr] == loc[objstr]:
continue
v = loc[objstr]
t = str(type(v))[7:-2]
print '%10s : value %10s, id %9i, type %-10s' % \
(objstr, v, id(v), t)
if type(v) == type([]):
for i in range(len(v)):
t = str(type(v[i]))[7:-2]
print '%7s[%i] : value %10s, id %9i, type %-10s' % \
(objstr, i, v[i], id(v[i]), t)
#==========================================================================
# Don't use "m * [n * [0]]" to create an array. Details follow.
# Two objects are the same if and only if they have the same id number.
# in the first example, x and y are different objects with the same value
# [0]. The "is" operator compares the ids.
print 'Example 1.'
watch.watchall('x = y = [0]')
# In this example, x and y are different objects.
watch.watchall('x = [0]; y = [0]')
x = y = 1
print 'For "x = y = 0", "x is y" returns %i.' % (x is y)
x = [0]; y = [0]
print 'For "x = [0]; y = [0]" , "x is y" returns %i.' % (x is y)
print """\nExample2. In Python, it is tempting to write
"arr = 3 * [4 * [0]]" to create a 3 by 4 array. But every newbie soon
learns not to do this but instead write a loop."""
x = 2 * [3 * [0]]
print x
x[0][0] = 1
print x
# I use the following which completely avoids the "*" construction.
print '\nExample 3.'
arr = []
for col in range(2):
row = []
for r in range(3):
row.append(0)
arr.append(row)
print arr
arr[0][0] = 1
print arr
#================================================================
# Mutable objects include variables, dictionaries and lists:
# x, {}, [0]
# Immutable objects include numbers, strings, and tuples:
# 3.14, 'asdf', (0, 1)
print """\nExample 4. The number of copies made of each mutable object is
determined lexically."""
# We want
# x = 7
# A = 2 * [x]
# to behave the same as
# x = 7
# A = [x, x]
# because there is only one x in A = 2 * [x]. Similarly for A = B = x.
print 'Examples where there is only one object.'
watch.watchall1('a = 7; x = 2 * [a]')
watch.watchall1('b = 7; x = y = b')
command = \
"""class trivial:
pass
x = 2 * [trivial()]
"""
watch.watchall1(command)
print 'Examples where there are two objects.'
watch.watchall1('a = 7; x = [a, a]')
watch.watchall1('x = 7; y = 8')
command = \
"""class trivial:
pass
x = [trivial(), trivial()]
"""
watch.watchall(command)
print """\nExample 5. The implementation chooses how many copies are made of
immutable objects."""
watch.watchall1('xx = [(0,1), (0,1)]')
watch.watchall1('xx = [3.14, 3.14]')
print """\nExample 6. Assignment gives the object on the left hand side the
value of the object on the right hand side."""
watch.watchall('a = 7; x = a')
print """\nExample 7. An assignment to a sub-object of a mutable object does
not change the mutable object (it changes its _content_). The id numbers
assigned while executing the following two lines need not be the same,
but they are on my system."""
watch.watchall1('x = [3.14, 99]')
watch.watchall1('x = [3.14, 99]; x[0] = 98')
print """\nExample 8. An assignment to a sub-object of a mutable object
is allowed. In the example, I am not changing the immutable objects, I am
replacing them."""
command = """x = [[], 'asdf', (0,1)]
for i in (0,1,2):
x[i] = 2*i
"""
watch.watchall1(command)
print """\nExample 9. An assignment to a sub-object of an immutable object
is forbidden."""
x = (3.14, 99)
print x
try:
x[0] = 2.718
except Exception, y:
print str(y)
print """\nExample 10. Assignment to a sub-object of a mutable sub-object of
an immutable object is allowed ( x = ([0],); x[0][0] = 1 )."""
watch.watchall1('x = ([0, 1], 3); x[0][0] = 77;')