Suggested Homework #9 (for Quiz #9 on 11/28/05)

Problem 1:

Consider the Python code below:


def checkargtype(id,type):
  def decorator(f):
    def decorated(*args,**kwargs):
      if not(isinstance(args[id],type)):    
        raise Exception("Arg %d (%s) not of type %s" % (id,repr(args[id]),repr(type)))
      return f(*args,**kwargs)
    decorated.__name__ = f.__name__
    return decorated
  return decorator

def checkreturntype(type):
  def decorator(f):
    def decorated(*args,**kwargs):
      rv=f(*args,**kwargs)
      if not(isinstance(rv,type)):
          raise Exception("Return value (%s) not of type %s" % (repr(rv),repr(type)))
      return rv
    decorated.__name__=f.__name__
    return decorated
  return decorator    





@checkargtype(0,str)
@checkreturntype(type(None))
def printstring(s):
    print s

@checkargtype(0,list)
@checkargtype(1,int)
@checkargtype(2,str)
@checkreturntype(tuple)
def foo(s1,n,s2,f):
    return f([s1*n,s2])

What is the output (what is printed, what is returned, and what, if any, exceptions are raised) for each of the following:

>>> printstring("foo")
>>> printstring(1)
>>> printstring()
>>> foo([1,2,3],2,"test",tuple)
>>> foo([1,2,3],"hmm...","test",tuple)
>>> foo({},{},{},{})

Problem 2:

Consider the Python code below:

class EmptyNodeType(object):
    def __iter__(self): return self
    def itera(self):    return self
    def iterb(self):    return self            
    def iterc(self):    return self
    def next(self):     raise StopIteration

EmptyNode=EmptyNodeType()

class Node(object):
    def __init__(self,val,left=EmptyNode,right=EmptyNode):
        self.left=left
        self.right=right
        self.val=val
    def __iter__(self):
        return self.itera()
    def itera(self):
        yield(self.val)
        for i in self.left.itera():  yield(i)
        for i in self.right.itera(): yield(i)
    def iterb(self):
        for i in self.left.iterb():  yield(i)
        yield(self.val)
        for i in self.right.iterb(): yield(i)
    def iterc(self):
        for i in self.left.iterc():  yield(i)
        for i in self.right.iterc(): yield(i)
        yield(self.val)            

class Tree(object):
    def __init__(self):
        self.__root=EmptyNode
    def __iter__(self): return self.itera()
    def itera(self):    return self.__root.itera()
    def iterb(self):    return self.__root.iterb()
    def iterc(self):    return self.__root.iterc()
    def insert(self,val):
        p=None
        n=self.__root
        while(n!=EmptyNode):
            p=n
            if val<n.val: n=n.left
            else:         n=n.right
        if p==None:
            self.__root=Node(val)
        elif val<p.val:
            p.left=Node(val)
        else:
            p.right=Node(val)

What is the output (what is printed, what is returned, and what, if any, exceptions are raised) for each of the following:

>>> t=Tree()
>>> for x in [10,5,8,2,6,1,9,3]: t.insert(x)
...
>>> t
>>> for x in t: print x
...
>>> iter(t)
>>> for x in iter(t): print x
...
>>> iter(iter(t))
>>> for x in iter(iter(t)): print x
...
>>> t.itera()
>>> for x in t.itera(): print x
...
>>> for x in zip(t.itera(),t.iterb(),t.iterc()): print x
...