class <name>(superclass,...):       # Assign to name.
    data = value                    # Shared class data
    def method(self,...):           # Methods
        self.member = value         # Per-instance data



>>> class SharedData:
...     spam = 42               # Generates a class data attribute.
...
>>> x = SharedData()            # Make two instances.
>>> y = SharedData()
>>> x.spam, y.spam              # They inherit and share spam.
(42, 42)



>>> SharedData.spam = 99
>>> x.spam, y.spam, SharedData.spam
(99, 99, 99)



>>> x.spam = 88
>>> x.spam, y.spam, SharedData.spam
(88, 99, 99)



class MixedNames:                          # Define class.
    data = 'spam'                          # Assign class attr.
    def __init__(self, value):              # Assign method name.
        self.data = value                   # Assign instance attr.
    def display(self):
        print self.data, MixedNames.data    # Instance attr, class attr



>>> x = MixedNames(1)           # Make two instance objects.
>>> y = MixedNames(2)           # Each has its own data.
>>> x.display(); y.display()    # self.data differs, Subclass.data same.
1 spam
2 spam



instance.method(args...)

class.method(instance, args...)



class NextClass:                            # Define class.
    def printer(self, text):                # Define method.
        self.message = text                 # Change instance.
        print self.message                  # Access instance.



>>> x = NextClass()                         # Make instance

>>> x.printer('instance call')              # Call its method
instance call

>>> x.message                               # Instance changed
'instance call'



>>> NextClass.printer(x, 'class call')      # Direct class call
class call

>>> x.message                               # Instance changed again
'class call'

>>> NextClass.printer('bad call')
TypeError: unbound method printer() must be called with NextClass instance...



class Super:
    def __init__(self, x): 
        ...default code...

class Sub(Super):
    def __init__(self, x, y):
        Super.__init__(self, x)          # Run superclass init.
        ...custom code...                # Do my init actions.

I = Sub(1, 2)



>>> class Super:
...     def method(self):
...         print 'in Super.method'
... 
>>> class Sub(Super):
...     def method(self):                       # Override method.
...         print 'starting Sub.method'         # Add actions here.
...         Super.method(self)                  # Run default action.
...         print 'ending Sub.method'
...



>>> x = Super()            # Make a Super instance.
>>> x.method()             # Runs Super.method
in Super.method

>>> x = Sub()              # Make a Sub instance.
>>> x.method()             # Runs Sub.method, which calls Super.method
starting Sub.method
in Super.method
ending Sub.method
 


# specialize.py

class Super:
    def method(self):
        print 'in Super.method'       # Default behavior
    def delegate(self):
        self.action()                 # Expected to be defined

class Inheritor(Super):               # Inherit method verbatim.
    pass

class Replacer(Super):                # Replace method completely.
    def method(self):
        print 'in Replacer.method'

class Extender(Super):                # Extend method behavior.
    def method(self):
        print 'starting Extender.method'
        Super.method(self)
        print 'ending Extender.method'

class Provider(Super):                # Fill in a required method.
    def action(self):
        print 'in Provider.action'

if __name__ == '__main__':
    for klass in (Inheritor, Replacer, Extender):
        print '\n' + klass.__name__ + '...'
        klass().method()

    print '\nProvider...'
    x = Provider()
    x.delegate()



% python specialize.py

Inheritor...
in Super.method

Replacer...
in Replacer.method

Extender...
starting Extender.method
in Super.method
ending Extender.method

Provider...
in Provider.action



class Super:
    def method(self):
        print 'in Super.method'
    def delegate(self):
        self.action()
    def action(self):
        assert 0, 'action must be defined!'



# number.py

class Number:
    def __init__(self, start):              # On Number(start)
        self.data = start
    def __sub__(self, other):               # On instance - other
        return Number(self.data - other)    # result is a new instance

>>> from number import Number               # Fetch class from module.
>>> X = Number(5)                           # Number.__init__(X, 5)
>>> Y = X - 2                               # Number.__sub__(X, 2)
>>> Y.data                                  # Y is new Number instance.
3





>>> class indexer:
...     def __getitem__(self, index):
...         return index ** 2
...
>>> X = indexer()
>>> X[2]                        # X[i] calls __getitem__(X, i).4
>>> for i in range(5): 
...     print X[i],             
...
0 1 4 9 16



>>> class stepper:
...     def __getitem__(self, i):
...         return self.data[i]
...
>>> X = stepper()              # X is a stepper object.
>>> X.data = "Spam"
>>>
>>> X[1]                       # Indexing calls __getitem__.
'p'
>>> for item in X:             # for loops call __getitem__.
...     print item,            # for indexes items 0..N.
...
S p a m



>>> 'p' in X                   # All call __getitem__ too.
1

>>> [c for c in X]             # List comprehension
['S', 'p', 'a', 'm']

>>> map(None, X)               # map calls
['S', 'p', 'a', 'm']

>>> (a, b, c, d) = X              # Sequence assignments
>>> a, c, d
('S', 'a', 'm')

>>> list(X), tuple(X), ''.join(X)
(['S', 'p', 'a', 'm'], ('S', 'p', 'a', 'm'), 'Spam')

>>> X
<__main__.stepper instance at 0x00A8D5D0>



# iters.py

class Squares:
    def __init__(self, start, stop):        # save state when created
        self.value = start - 1
        self.stop  = stop
    def __iter__(self):                     # get iterator object on iter()
        return self
    def next(self):                         # return a square on each iteration
        if self.value == self.stop:
            raise StopIteration
        self.value += 1
        return self.value ** 2


% python
>>> from iters import Squares
>>> for i in Squares(1, 5):          # for calls iter(), which calls __iter__()
...     print i,                     # each iteration calls next()
...
1 4 9 16 25



>>> X = Squares(1, 5)
>>> X[1]
AttributeError: Squares instance has no attribute '__getitem__'



>>> X = Squares(1, 5)
>>> [n for n in X]                     # Exhausts items
[1, 4, 9, 16, 25]
>>> [n for n in X]                     # Now it's empty.
[]
>>> [n for n in Squares(1, 5)]         # make a new iterator object
[1, 4, 9, 16, 25]
>>> list(Squares(1, 3))
[1, 4, 9]



>>> from __future__ import generators     # Needed in Python 2.2, but not later
>>>
>>> def gsquares(start, stop):
...     for i in range(start, stop+1):
...         yield i ** 2
...
>>> for i in gsquares(1, 5):
...     print i,
...
1 4 9 16 25



>>> [x ** 2 for x in range(1, 6)]
[1, 4, 9, 16, 25]



>>> S = 'ace'
>>> for x in S:
        for y in S:
            print x + y,
		
aa ac ae ca cc ce ea ec ee



# skipper.py

class SkipIterator:
    def __init__(self, wrapped):
        self.wrapped = wrapped                      # iterator state information
        self.offset  = 0
    def next(self):
        if self.offset >= len(self.wrapped):        # terminate iterations
            raise StopIteration
        else:
            item = self.wrapped[self.offset]        # else return and skip
            self.offset += 2
            return item

class SkipObject:
    def __init__(self, wrapped):                    # save item to be used
        self.wrapped = wrapped
    def __iter__(self):
        return SkipIterator(self.wrapped)           # new iterator each time

if __name__ == '__main__':
    alpha = 'abcdef'
    skipper = SkipObject(alpha)                     # make container object
    I = iter(skipper)                               # make an iterator on it
    print I.next(), I.next(), I.next()              # visit offsets 0, 2, 4
    
    for x in skipper:               # for calls __iter__ automatically
        for y in skipper:           # nested fors call __iter__ again each time
            print x + y,            # each iterator has its own state, offset



% python skipper.py
a c e
aa ac ae ca cc ce ea ec ee



>>> S = 'abcdef'
>>> for x in S[::2]:
        for y in S[::2]:              # new objects on each iteration
            print x + y,
		
aa ac ae ca cc ce ea ec ee



>>> S = 'abcdef'
>>> S = S[::2]
>>> S
'ace'
>>> for x in S:
        for y in S:                   # same object, new iterators
            print x + y,
		
aa ac ae ca cc ce ea ec ee



>>> class empty:
...     def __getattr__(self, attrname):
...         if attrname == "age":
...             return 40
...         else:
...             raise AttributeError, attrname
...
>>> X = empty()
>>> X.age
40
>>> X.name
...error text omitted...
AttributeError: name



>>> class accesscontrol:
...     def __setattr__(self, attr, value):
...         if attr == 'age':
...             self.__dict__[attr] = value
...         else:
...             raise AttributeError, attr + ' not allowed'
...
>>> X = accesscontrol()
>>> X.age = 40                     # Calls __setattr__
>>> X.age
40
>>> X.name = 'mel'
...text omitted...
AttributeError: name not allowed



class PrivateExc(Exception): pass                # more on exceptions later

class Privacy:
    def __setattr__(self, attrname, value):      # on self.attrname = value
        if attrname in self.privates:
            raise PrivateExc(attrname, self)
        else:
            self.__dict__[attrname] = value      # self.attrname = value loops!

class Test1(Privacy):
    privates = ['age']

class Test2(Privacy):
    privates = ['name', 'pay']
    def __init__(self):
        self.__dict__['name'] = 'Tom'

x = Test1()
y = Test2()

x.name = 'Bob'
y.name = 'Sue'   # <== fails

y.age  = 30
x.age  = 40      # <== fails



>>> class adder:
...     def __init__(self, value=0):
...         self.data = value                  # Initialize data.
...     def __add__(self, other):
...         self.data += other                 # Add other in-place.

>>> class addrepr(adder):                      # Inherit __init__, __add__.
...     def __repr__(self):                    # Add string representation.
...         return 'addrepr(%s)' % self.data   # Convert to string as code.

>>> x = addrepr(2)              # Runs __init__
>>> x + 1                       # Runs __add__
>>> x                           # Runs __repr__
addrepr(3)
>>> print x                     # Runs __repr__
addrepr(3)
>>> str(x), repr(x)             # Runs   __repr__
('addrepr(3)', 'addrepr(3)')



>>> class addstr(adder):            
...     def __str__(self):                     # __str__ but no __repr__
...         return '[Value: %s]' % self.data   # Convert to nice string.

>>> x = addstr(3)
>>> x + 1
>>> x                                          # Default repr
<__main__.addstr instance at 0x00B35EF0>
>>> print x                                    # Runs __str__
[Value: 4]
>>> str(x), repr(x)
('[Value: 4]', '<__main__.addstr instance at 0x00B35EF0>')



>>> class addboth(adder):
...     def __str__(self):
...         return '[Value: %s]' % self.data   # User-friendly string
...     def __repr__(self):
...         return 'addboth(%s)' % self.data   # As-code string

>>> x = addboth(4)
>>> x + 1
>>> x                                  # Runs __repr__
addboth(5)
>>> print x                            # Runs __str__
[Value: 5]
>>> str(x), repr(x)
('[Value: 5]', 'addboth(5)')



>>> class Commuter:
...     def __init__(self, val):
...         self.val = val
...     def __add__(self, other):
...         print 'add', self.val, other
...     def __radd__(self, other):
...         print 'radd', self.val, other
...
>>> x = Commuter(88)
>>> y = Commuter(99)
>>> x + 1                      # __add__:  instance + noninstance
add 88 1
>>> 1 + y                      # __radd__: noninstance + instance
radd 99 1
>>> x + y                      # __add__:  instance + instance
add 88 <__main__.Commuter instance at 0x0086C3D8>



>>> class Prod:
...     def __init__(self, value):
...         self.value = value
...     def __call__(self, other):
...         return self.value * other
...
>>> x = Prod(2)
>>> x(3)
6
>>> x(4)
8



>>> class Prod:
...     def __init__(self, value):
...         self.value = value
...     def comp(self, other):
...         return self.value * other
...
>>> x = Prod(3)
>>> x.comp(3)
9
>>> x.comp(4)
12



class Callback:
    def __init__(self, color):               # function + state information
        self.color = color
    def __call__(self):                      # support calls with no arguments
        print 'turn', self.color


cb1 = Callback('blue')                       # 'remember' blue
cb2 = Callback('green')

B1 = Button(command=cb1)                     # register handlers
B2 = Button(command=cb2)                     # register handlers


cb1()                                        # on events: prints 'blue'
cb2()                                        # prints 'green'



cb3 = (lambda color='red': 'turn ' + color)  # or: defaults
print cb3()



class Callback:
    def __init__(self, color):               # class with state information
        self.color = color
    def changeColor(self):                   # a normal named method
        print 'turn', self.color

cb1 = Callback('blue')
cb2 = Callback('yellow')

B1 = Button(command=cb1.changeColor)         # reference, but don't call 
B2 = Button(command=cb2.changeColor)         # remembers function+self 



object = Callback('blue')
cb = object.changeColor                      # registered event handler
cb()                                         # on event prints 'blue'



>>> class Life:
...     def __init__(self, name='unknown'):
...         print 'Hello', name
...         self.name = name
...     def __del__(self):
...         print 'Goodbye', self.name
...
>>> brian = Life('Brian')
Hello Brian
>>> brian = 'loretta'
Goodbye Brian



# manynames.py

X = 11                      # Global (module) name/attribute (X, or manynames.X)

def f():
    print X                 # Access global X (11)

def g():
    X = 22                  # Local (function) variable (X, hides module X)
    print X

class C:
    X = 33                  # Class attribute (C.X)
    def m(self):
        X = 44              # Local variable in method (X)
        self.X = 55         # Instance attribute (instance.X)


# manynames.py, continued

if __name__ == '__main__':
    print X                 # 11: module (a.k.a. manynames.X outside file)
    f()                     # 11: global
    g()                     # 22: local
    print X                 # 11: module name unchanged

    obj = C()               # make instance
    print obj.X             # 33: class name inherited by instance

    obj.m()                 # attach attribute name X to instance now
    print obj.X             # 55: instance
    print C.X               # 33: class (a.k.a. obj.X if no X in instance)

    #print c.m.X            # FAILS: only visible in method
    #print f.X              # FAILS: only visible in function



# otherfile.py

import manynames

X = 66
print X                 # 66: the global here
print manynames.X       # 11: globals become attributes after imports

manynames.f()           # 11: manynames' X, not the one here!
manynames.g()           # 22: local in other file's function

print manynames.C.X     # 33: attribute of class in other module
I = manynames.C()
print I.X               # 33: still from class here
I.m()
print I.X               # 55: now from instance!



>>> class super:
...     def hello(self):
...         self.data1 = 'spam'
...
>>> class sub(super):
...     def hola(self):
...         self.data2 = 'eggs'



>>> X = sub()
>>> X.__dict__
{  }

>>> X.__class__
<class __main__.sub at 0x00A48448>

>>> sub.__bases__
(<class __main__.super at 0x00A3E1C8>,)

>>> super.__bases__
()



>>> Y = sub()

>>> X.hello()
>>> X.__dict__
{'data1': 'spam'}

>>> X.hola()
>>> X.__dict__
{'data1': 'spam', 'data2': 'eggs'}
 
>>> sub.__dict__
{'__module__': '__main__', '__doc__': None, 'hola': <function hola at
 0x00A47048>}

>>> super.__dict__
{'__module__': '__main__', 'hello': <function hello at 0x00A3C5A8>,
 '__doc__': None}

>>> sub.__dict__.keys(), super.__dict__.keys()
(['__module__', '__doc__', 'hola'], ['__module__', 'hello', '__doc__'])

>>> Y.__dict__
{  }



>>> X.data1, X.__dict__['data1']
('spam', 'spam')

>>> X.data3 = 'toast'
>>> X.__dict__
{'data1': 'spam', 'data3': 'toast', 'data2': 'eggs'}

>>> X.__dict__['data3'] = 'ham'
>>> X.data3
'ham'



>>> X.__dict__
{'data1': 'spam', 'data3': 'ham', 'data2': 'eggs'}
>>> X.__dict__.keys()
['data1', 'data3', 'data2']

>>>> dir(X)
['__doc__', '__module__', 'data1', 'data2', 'data3', 'hello', 'hola']
>>> dir(sub)
['__doc__', '__module__', 'hello', 'hola']
>>> dir(super)
['__doc__', '__module__', 'hello']



# classtree.py 

def classtree(cls, indent):
    print '.'*indent, cls.__name__        # Print class name here.
    for supercls in cls.__bases__:        # Recur to all superclasses
        classtree(supercls, indent+3)         # May visit super > once

def instancetree(inst):
    print 'Tree of', inst                     # Show instance.
    classtree(inst.__class__, 3)          # Climb to its class.

def selftest():
    class A: pass
    class B(A): pass
    class C(A): pass
    class D(B,C): pass
    class E: pass
    class F(D,E): pass
    instancetree(B())
    instancetree(F())
    
if __name__ == '__main__': selftest()



% python classtree.py
Tree of <__main__.B instance at 0x00ACB438>
... B
...... A
Tree of <__main__.F instance at 0x00AC4DA8>
... F
...... D
......... B
............ A
......... C
............ A
...... E



>>> class Emp: pass
>>> class Person(Emp): pass
>>> bob = Person()
>>> import classtree
>>> classtree.instancetree(bob)
Tree of <__main__.Person instance at 0x00AD34E8>
... Person
...... Emp



# person.py 

class GenericDisplay:
    def gatherAttrs(self):
        attrs = '\n'
        for key in self.__dict__:
            attrs += '\t%s=%s\n' % (key, self.__dict__[key])
        return attrs
    def __str__(self):
        return '<%s: %s>' % (self.__class__.__name__, self.gatherAttrs())

class Person(GenericDisplay):
    def __init__(self, name, age):
        self.name = name
        self.age  = age
    def lastName(self):
        return self.name.split()[-1]
    def birthDay(self):
        self.age += 1

class Employee(Person):
    def __init__(self, name, age, job=None, pay=0):
        Person.__init__(self, name, age)
        self.job  = job
        self.pay  = pay
    def birthDay(self):
        self.age += 2
    def giveRaise(self, percent):
        self.pay *= (1.0 + percent)

if __name__ == '__main__':
    bob = Person('Bob Smith', 40)
    print bob
    print bob.lastName()
    bob.birthDay()
    print bob
    
    sue = Employee('Sue Jones', 44, job='dev', pay=100000)
    print sue
    print sue.lastName()
    sue.birthDay()
    sue.giveRaise(.10)
    print sue



>>> from person import Person
>>> ann = Person('Ann Smith', 45)
>>> ann.lastName()
'Smith'
>>> ann.birthDay()
>>> ann.age
46
>>> print ann
<Person: 
	age=46
	name=Ann Smith
>



% python person.py
<Person: 
	age=40
	name=Bob Smith
>
Smith
<Person: 
	age=41
	name=Bob Smith
>
<Employee: 
	job=dev
	pay=100000
	age=44
	name=Sue Jones
>
Jones
<Employee: 
	job=dev
	pay=110000.0
	age=46
	name=Sue Jones
>

