# setwrapper.py

class Set:
   def __init__(self, value = []):    # Constructor
       self.data = []                 # Manages a list
       self.concat(value)

   def intersect(self, other):        # other is any sequence.
       res = []                       # self is the subject.
       for x in self.data:
           if x in other:             # Pick common items.
               res.append(x)
       return Set(res)                # Return a new Set.

   def union(self, other):            # other is any sequence.
       res = self.data[:]             # Copy of my list
       for x in other:                # Add items in other.
           if not x in res:
               res.append(x)
       return Set(res)

   def concat(self, value):           # value: list, Set...
       for x in value:                # Removes duplicates
          if not x in self.data:
               self.data.append(x)

   def __len__(self):          return len(self.data)        # len(self)
   def __getitem__(self, key): return self.data[key]        # self[i]
   def __and__(self, other):   return self.intersect(other) # self & other
   def __or__(self, other):    return self.union(other)     # self | other
   def __repr__(self):         return 'Set:' + `self.data`  # Print



# typesubclass.py

# Subclass built-in list type/class.
# Map 1..N to 0..N-1; call back to built-in version.

class MyList(list):
    def __getitem__(self, offset):
        print '(indexing %s at %s)' % (self, offset)  
        return list.__getitem__(self, offset - 1) 

if __name__ == '__main__':
    print list('abc')
    x = MyList('abc')               # __init__ inherited from list
    print x                         # __repr__ inherited from list

    print x[1]                      # MyList.__getitem__
    print x[3]                      # Customizes list superclass method

    x.append('spam'); print x       # Attributes from list superclass
    x.reverse();      print x



% python typesubclass.py
['a', 'b', 'c']
['a', 'b', 'c']
(indexing ['a', 'b', 'c'] at 1)
a
(indexing ['a', 'b', 'c'] at 3)
c
['a', 'b', 'c', 'spam']
['spam', 'c', 'b', 'a']



# setsubclass.py

class Set(list):
    def __init__(self, value = []):      # Constructor
        list.__init__([])                # Customizes list
        self.concat(value)               # Copies mutable defaults

    def intersect(self, other):          # other is any sequence.
        res = []                         # self is the subject.
        for x in self:
            if x in other:               # Pick common items.
                res.append(x)
        return Set(res)                  # Return a new Set.

    def union(self, other):              # other is any sequence.
        res = Set(self)                  # Copy me and my list.
        res.concat(other)
        return res

    def concat(self, value):             # value: list, Set...
        for x in value:                  # Removes duplicates
            if not x in self:
                self.append(x)

    def __and__(self, other): return self.intersect(other)
    def __or__(self, other):  return self.union(other)
    def __repr__(self):       return 'Set:' + list.__repr__(self)

if __name__ == '__main__':
    x = Set([1,3,5,7])
    y = Set([2,1,4,5,6])
    print x, y, len(x)
    print x.intersect(y), y.union(x)
    print x & y, x | y
    x.reverse(); print x



% python setsubclass.py
Set:[1, 3, 5, 7] Set:[2, 1, 4, 5, 6] 4
Set:[1, 5] Set:[2, 1, 4, 5, 6, 3, 7]
Set:[1, 5] Set:[1, 3, 5, 7, 2, 4, 6]
Set:[7, 5, 3, 1]



class C1:
    def meth1(self): self.X = 88         # Assume X is mine.
    def meth2(self): print self.X

class C2:
    def metha(self): self.X = 99         # Me too
    def methb(self): print self.X

class C3(C1, C2): ...
I = C3()                                 # Only 1 X in I!



# private.py

class C1:
    def meth1(self): self.__X = 88       # Now X is mine.
    def meth2(self): print self.__X      # Becomes _C1__X in I

class C2:
    def metha(self): self.__X = 99       # Me too
    def methb(self): print self.__X      # Becomes _C2__X in I

class C3(C1, C2): pass
I = C3()                                 # Two X names in I

I.meth1(); I.metha()
print I.__dict__
I.meth2(); I.methb()



% python private.py
{'_C2__X': 99, '_C1__X': 88}
88
99



class newstyle(object):
    ...normal code...



>>> class A:      attr = 1             # Classic
>>> class B(A):   pass
>>> class C(A):   attr = 2
>>> class D(B,C): pass                 # Tries A before C
>>> x = D()
>>> x.attr
1



>>> class A(object): attr = 1          # New style
>>> class B(A):      pass
>>> class C(A):      attr = 2
>>> class D(B,C):    pass              # Tries C before A
>>> x = D()
>>> x.attr
2



>>> class A:      attr = 1            # Classic
>>> class B(A):   pass
>>> class C(A):   attr = 2
>>> class D(B,C): attr = C.attr       # Choose C, to the right.
>>> x = D()
>>> x.attr                            # Works like new style
2



>>> class A(object): attr = 1         # New style
>>> class B(A):      pass
>>> class C(A):      attr = 2
>>> class D(B,C):    attr = B.attr    # Choose A.attr, above.
>>> x = D()
>>> x.attr                            # Works like classic
1



>>> class A:
...    def meth(s): print 'A.meth'
>>> class C(A):
...     def meth(s): print 'C.meth'
>>> class B(A): 
...     pass

>>> class D(B,C): pass                 # Use default search order.
>>> x = D()                          # Will vary per class type
>>> x.meth()                         # Defaults to classic order
A.meth

>>> class D(B,C): meth = C.meth      # Pick C's method: new style.
>>> x = D()
>>> x.meth()
C.meth

>>> class D(B,C): meth = B.meth      # Pick B's method: classic.
>>> x = D()
>>> x.meth()
A.meth



class D(B,C):
    def meth(self):                  # Redefine lower.        ...
        C.meth(self)                 # Pick C's method by calling.



>>> class limiter(object):
...     __slots__ = ['age', 'name', 'job']
        
>>> x = limiter()
>>> x.age                     # Must assign before use
AttributeError: age

>>> x.age = 40
>>> x.age
40
>>> x.ape = 1000              # Illegal: not in slots
AttributeError: 'limiter' object has no attribute 'ape'



>>> class classic:
...     def __getattr__(self, name):
...         if name == 'age':
...             return 40
...         else:
...             raise AttributeError
...        
>>> x = classic()
>>> x.age                                    # Runs __getattr__
40
>>> x.name                                   # Runs __getattr__
AttributeError



>>> class newprops(object):
...     def getage(self):
...         return 40
...     age = property(getage, None, None, None)      # get,set,del,docs
... 
>>> x = newprops()
>>> x.age                                    # Runs getage
40
>>> x.name                                   # Normal fetch
AttributeError: newprops instance has no attribute 'name'



>>> class newprops(object):
...     def getage(self):
...         return 40
...     def setage(self, value):
...         print 'set age:', value
...         self._age = value
...     age = property(getage, setage, None, None)
...
>>> x = newprops()
>>> x.age                     # Runs getage
40
>>> x.age = 42                # Runs setage 
set age: 42
>>> x._age                    # Normal fetch; no getage call
42
>>> x.job = 'trainer'         # Normal assign; no setage call
>>> x.job                     # Normal fetch; no getage call
'trainer'



>>> class classic:
...     def __getattr__(self, name):            # On undefined reference
...         if name == 'age':
...             return 40
...         else:
...             raise AttributeError
...     def __setattr__(self, name, value):     # On all assignments
...         print 'set:', name, value
...         if name == 'age':
...             self.__dict__['_age'] = value
...         else:
...             self.__dict__[name] = value
...
>>> x = classic()
>>> x.age                     # Runs __getattr__
40
>>> x.age = 41                # Runs __setattr__
set: age 41
>>> x._age                    # Defined: no __getattr__ call
41
>>> x.job = 'trainer'         # Runs __setattr__ again
>>> x.job                     # Defined: no __getattr__ call



# spam.py

class Spam:
    numInstances = 0
    def __init__(self):
        Spam.numInstances = Spam.numInstances + 1
    def printNumInstances():
        print "Number of instances created: ", Spam.numInstances



>>> from spam import *
>>> a = Spam()
>>> b = Spam()
>>> c = Spam()
>>> Spam.printNumInstances()
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: unbound method must be called with class instance 1st argument



def printNumInstances():
    print "Number of instances created: ", Spam.numInstances

class Spam:
    numInstances = 0
    def __init__(self):
        Spam.numInstances = Spam.numInstances + 1

>>> import spam
>>> a = spam.Spam()
>>> b = spam.Spam()
>>> c = spam.Spam()
>>> spam.printNumInstances()
Number of instances created:  3
>>> spam.Spam.numInstances
3



class Spam:
    numInstances = 0
    def __init__(self):
        Spam.numInstances = Spam.numInstances + 1
    def printNumInstances(self):
        print "Number of instances created: ", Spam.numInstances

>>> from spam import Spam
>>> a, b, c = Spam(), Spam(), Spam()
>>> a.printNumInstances()
Number of instances created:  3
>>> b.printNumInstances()
Number of instances created:  3
>>> Spam().printNumInstances()
Number of instances created:  4



class Multi:
    def imeth(self, x):          # Normal instance method
        print self, x
    def smeth(x):                # Static: no instance passed
        print x
    def cmeth(cls, x):           # Class: gets class, not instance
        print cls, x
    smeth = staticmethod(smeth)  # Make smeth a static method.
    cmeth = classmethod(cmeth)   # Make cmeth a class method.



>>> obj = Multi()              # Make an instance
>>> obj.imeth(1)                 # Normal call, through instance
<__main__.Multi instance...> 1
>>> Multi.imeth(obj, 2)          # Normal call, through class
<__main__.Multi instance...> 2



>>> Multi.smeth(3)             # Static call, through class
3
>>> obj.smeth(4)               # Static call, through instance
4



>>> Multi.cmeth(5)             # Class call, through class
__main__.Multi 5
>>> obj.cmeth(6)               # Class call, through instance
__main__.Multi 6



# spam2.py 

class Spam:
    numInstances = 0
    def __init__(self):
        Spam.numInstances += 1
    def printNumInstances():
        print "Number of instances:", Spam.numInstances
    printNumInstances = staticmethod(printNumInstances)

>>> a = Spam()
>>> b = Spam()
>>> c = Spam()
>>> Spam.printNumInstances()
Number of instances: 3
>>> a.printNumInstances()
Number of instances: 3



class C:
   @staticmethod
   def meth():
       ...


class C:
   def meth():
       ...
   meth = staticmethod(meth)   # rebind name 



@A @B @C
def f(): 
    ...


def f(): 
    ...
f = A(B(C(f)))



# decorator.py

class tracer:
    def __init__(self, func):
        self.calls = 0
        self.func  = func
    def __call__(self, *args):
        self.calls += 1
        print 'call %s to %s' % (self.calls, self.func.__name__)
        self.func(*args)

@tracer
def spam(a, b, c):            # wrap spam in a decorator object
    print a, b, c

spam(1, 2, 3)                 # really calls the tracer wrapper object
spam('a', 'b', 'c')           # invokes __call__ in class
spam(4, 5, 6)                 # __call__ adds logic and runs original object



call 1 to spam
1 2 3
call 2 to spam
a b c
call 3 to spam
4 5 6



>>> class X:
...     a = 1        # Class attribute
...
>>> I = X()
>>> I.a              # Inherited by instance
1
>>> X.a
1



>>> X.a = 2          # May change more than X
>>> I.a              # I changes too.
2
>>> J = X()          # J inherits from X's runtime values
>>> J.a              # (but assigning to J.a changes a in J, not X or I).
2



class X: pass                          # Make a few attribute namespaces.
class Y: pass

X.a = 1                                # Use class attributes as variables.
X.b = 2                                # No instances anywhere to be found
X.c = 3
Y.a = X.a + X.b + X.c

for X.i in range(Y.a): print X.i       # Prints 0..5



class Record: pass
X = Record()
X.name = 'bob'
X.job  = 'Pizza maker'



class Lister:
    def __repr__(self): ...

class Super:
    def __repr__(self): ...

class Sub(Lister, Super):  # Get Lister's __repr__ by listing it first.



class Lister:
    def __repr__(self): ...
    def other(self): ...

class Super:
    def __repr__(self): ...
    def other(self): ...

class Sub(Lister, Super):  # Get Lister's __repr__ by listing it first
    other = Super.other    # but explicitly pick Super's version of other.
    def __init__(self): 
        ... 

x = Sub()                  # Inheritance searches Sub before Super/Lister.



class Sub(Super, Lister):          # Get Super's other by order.
    __repr__ = Lister.__repr__  # Explicitly pick Lister.__repr__.



# nester.py 

def generate():
    class Spam:
        count = 1
        def method(self):        # Name Spam not visible:
            print Spam.count     # not local(def),global(module), built-in
    return Spam()

generate().method()




C:\python\examples> python nester.py
Traceback (innermost last):
  File "nester.py", line 8, in ?
    generate().method()
  File "nester.py", line 5, in method
    print Spam.count             # Not local(def),global(module), built-in
NameError: Spam



def generate():
    global Spam                 # Force Spam to module scope.
    class Spam:
        count = 1
        def method(self):
            print Spam.count        # Works: in global (enclosing module)
    return Spam()

generate().method()             # Prints 1



def generate():
    return Spam()

class Spam:                    # Define at module top-level.
    count = 1
    def method(self):
        print Spam.count       # Works: in global (enclosing module)

generate().method()



def generate():
    class Spam:
        count = 1
        def method(self):
            print self.__class__.count       # Works: qualify to get class
    return Spam()

generate().method()




class Lunch:
    def __init__(self)          # Make/embed Customer and Employee.
    def order(self, foodName)  # Start a Customer order simulation.
    def result(self)           # Ask the Customer what kind of Food it has.

class Customer:
    def __init__(self)                         # Initialize my food to None.
    def placeOrder(self, foodName, employee)  # Place order with an Employee.
    def printFood(self)                       # Print the name of my food.

class Employee:
    def takeOrder(self, foodName)       # Return a Food, with requested name.

class Food:
    def __init__(self, name)         # Store food name.




% python
>>> from zoo import Cat, Hacker
>>> spot = Cat()
>>> spot.reply()              # Animal.reply; calls Cat.speak
meow
>>> data = Hacker()           # Animal.reply; calls Primate.speak
>>> data.reply()
Hello world!




% python
>>> import parrot
>>> parrot.Scene().action()       # Activate nested objects.
customer: "that's one ex-bird!"
clerk: "no it isn't..."
parrot: None


