# "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;')












Make your own free website on Tripod.com