class <nazwa>(klasa_nadrzędna, ...):         # Przypisanie do nazwy
   data = value                              # Współdzielone dane klasy
   def method(self, ...):                    # Metody
      self.member = value                    # Dane instancji


>>> class SharedData:
...    spam = 42                             # Wygenerowanie atrybutu danych klasy
...
>>> x = SharedData( )                        # Utworzenie dwóch instancji
>>> y = SharedData( )
>>> x.spam, y.spam                           # Dziedziczą i współdzielą zmienną 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:                            # Zdefiniowanie klasy
   data = 'mielonka'                         # Przypisanie atrybutu klasy
   def __init__(self, value):                # Przypisanie nazwy metody
      self.data = value                      # Przypisanie atrybutu instancji
   def display(self):
      print self.data, MixedNames.data       # Atrybut instancji, atrybut klasy


>>> x = MixedNames(1)                        # Utworzenie dwóch obiektów instancji
>>> y = MixedNames(2)                        # Każdy ma własne dane
>>> x.display( ); y.display( )               # self.data jest inny, Subclass.data jest tym samym
1 mielonka
2 mielonka


instancja.metoda(argumenty...)


klasa.metoda(instancja, argumenty...)


class NextClass:                             # Zdefiniowanie klasy
   def printer(self, text):                  # Zdefiniowanie metody
      self.message = text                    # Modyfikacja instancji
      print self.message                     # Dostęp do instancji


>>> x = NextClass( )                         # Utworzenie instancji

>>> x.printer('wywołanie instancji')         # Wywołanie jej metody
wywołanie instancji

>>> x.message                                # Modyfikacja instancji
'wywołanie instancji'


>>> NextClass.printer(x, 'wywołanie klasy')  # Bezpośrednie wywołanie klasy
wywołanie klasy

>>> x.message                                # Ponowna modyfikacja instancji
'wywołanie klasy'


>>> NextClass.printer('złe wywołanie')
TypeError: unbound method printer( ) must be called with NextClass instance...


class Super:
   def __init__(self, x):
      ...kod domyślny...

class Sub(Super):
   def __init__(self, x, y):
      Super.__init__(self, x)                # Wykonanie metody __init__ klasy nadrzędnej
      ...własny kod...                       # Wykonanie własnych działań inicjalizacyjnych

I = Sub(1, 2)


>>> class Super:
...    def method(self):
...       print 'w Super.method'
...
>>> class Sub(Super):
...    def method(self):                     # Przesłonienie metody
...       print 'początek Sub.method'        # Dodanie działań
...       Super.method(self)                 # Wykonanie działania domyślnego
...       print 'koniec Sub.method'
...


>>> x = Super( )                             # Utworzenie instancji klasy Super
>>> x.method( )                              # Wykonanie metody Super.method
w Super.method

>>> x = Sub( )                               # Utworzenie instancji klasy Sub
>>> x.method( )                              # Wykonuje Sub.method, co wywołuje Super.method
początek Sub.method
w Super.method
koniec Sub.method


# Plik specialize.py

# -*- coding: utf-8 -*-
class Super:
   def method(self):
      print 'w Super.method'                 # Zachowanie domyślne
   def delegate(self):
      self.action( )                         # Oczekuje zdefiniowania

class Inheritor(Super):                      # Odziedziczenie wszystkich metod
   pass

class Replacer(Super):                       # Całkowite zastąpienie metody method
   def method(self):
      print 'w Replacer.method'

class Extender(Super):                       # Rozszerzenie metody method
   def method(self):
      print 'początek Extender.method'
      Super.method(self)
      print 'koniec Extender.method'

class Provider(Super):                       # Uzupełnienie wymaganej metody
   def action(self):
      print 'w 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...
w Super.method

Replacer...
w Replacer.method

Extender...
początek Extender.method

in Super.method
koniec Extender.method

Provider...
w Provider.action


class Super:
   def method(self):
      print 'w Super.method'
   def delegate(self):
      self.action( )
   def action(self):
      assert 0, 'działanie musi zostać zdefiniowane!'


# Plik number.py

# -*- coding: utf-8 -*-
class Number:
   def __init__(self, start):                # Przy wywołaniu Number(start)
      self.data = start
   def __sub__(self, other):                 # Przy instancji i other
      return Number(self.data - other)       # Wynik jest nową instancją


>>> from number import Number                # Pobranie klasy z modułu
>>> X = Number(5)                            # Number.__init__(X, 5)
>>> Y = X - 2                                # Number.__sub__(X, 2)
>>> Y.data                                   # Y to nowa instancja klasy Number
3


>>> class indexer:
...    def __getitem__(self, index):
...       return index ** 2
...
>>> X = indexer( )
>>> X[2]                                     # X[i] wywołuje __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 jest instancją klasy stepper
>>> X.data = "Mielonka"
>>>
>>> X[1]                                     # Indeksowanie wywołuje __getitem__
'i'
>>> for item in X:                           # Pętla for wywołuje metodę __getitem__
...    print item,                           # Pętla for indeksuje elementy 0..N
...
M i e l o n k a


>>> 'i' in X                                 # Wszystkie wywołują metodę __getitem__
True

>>> [c for c in X]                           # Lista składana
['M', 'i', 'e', 'l', 'o', 'n', 'k', 'a']

>>> map(None, X)                             # Wywołanie funkcji map
['M', 'i', 'e', 'l', 'o', 'n', 'k', 'a']

>>> (a, b, c, d, e, f, g, h) = X             # Przypisania sekwencji
>>> a, c, e
('M', 'e', 'o')

>>> list(X), tuple(X), ''.join(X)
(['M', 'i', 'e', 'l', 'o', 'n', 'k', 'a'], ('M', 'i', 'e', 'l', 'o', 'n', 'k', 'a'), 'Mielonka')

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


# Plik iters.py

# -*- coding: utf-8 -*-
class Squares:
   def __init__(self, start, stop):          # Zapisanie stanu przy utworzeniu
      self.value = start - 1
      self.stop = stop
   def __iter__(self):                       # Otrzymanie obiektu iteratora na iter( )
      return self
   def next(self):                           # Zwrócenie kwadratu z każdą iteracją
      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 wywołuje metodę iter(),  która wywołuje __iter__()
...    print i,                              # Każda iteracja wywołuje metodę 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]                           # Wyczerpuje elementy
[1, 4, 9, 16, 25]
>>> [n for n in X]                           # Teraz jest pusta 
[]
>>> [n for n in Squares(1, 5)]               # Utworzenie nowego obiektu iteratora
[1, 4, 9, 16, 25]
>>> list(Squares(1, 3))
[1, 4, 9]


>>> from __future__ import generators        # Potrzebne w Pythonie 2.2, później już nie
>>>
>>> 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


# Plik skipper.py

# -*- coding: utf-8 -*-
class SkipIterator:
   def __init__(self, wrapped):
      self.wrapped = wrapped                 # Informacje o stanie iteratora
      self.offset = 0
   def next(self):
      if self.offset >= len(self.wrapped):   # Zakończenie iteracji
         raise StopIteration
      else:
         item = self.wrapped[self.offset]    # Inaczej zwrócenie elementu i pominięcie
         self.offset += 2
         return item

class SkipObject:
   def __init__(self, wrapped):              # Zapisanie elementu, który ma być użyty
      self.wrapped = wrapped
   def __iter__(self):
      return SkipIterator(self.wrapped)      # Za każdym razem nowy iterator

if __name__ == '__main__':
   alpha = 'abcdef'
   skipper = SkipObject(alpha)               # Utworzenie obiektu pojemnika
   I = iter(skipper)                         # Utworzenie na nim iteratora
   print I.next(), I.next( ), I.next( )      # Odwiedzenie wartości przesunięcia 0, 2, 4

   for x in skipper:                            # for automatycznie wywołuje __iter__
      for y in skipper:                         # Zagnieżdżone for za każdym razem wywołują __iter__
         print x + y,                           # Każdy iterator ma własny stan i przesunięcie


% 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]:                      # Nowe obiekty w każdej iteracji
...       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:                           # Ten sam obiekt, nowe iteratory
...       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
...pominięto tekst błędu...
AttributeError: name


>>> class accesscontrol:
...    def __setattr__(self, attr, value):
...       if attr == 'age':
...          self.__dict__[attr] = value
...       else:
...          raise AttributeError, attr + ' nie jest dozwolony'
...
>>> X = accesscontrol( )
>>> X.age = 40                               # Wywołuje __setattr__
>>> X.age
40
>>> X.name = 'amadeusz'
...tekst pominięto...
AttributeError: name nie jest dozwolony


class PrivateExc(Exception): pass            # Więcej o wyjątkach później

class Privacy:
   def __setattr__(self, attrname, value):   # Dla self.attrname = value
      if attrname in self.privates:
         raise PrivateExc(attrname, self)
      else:
         self.__dict__[attrname] = value     # Pętla Self.attrname = value!

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

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

x = Test1( )
y = Test2( )

x.name = 'Edward'
y.name = 'Ernest'                            # <== porażka

y.age = 30
x.age = 40                                   # <== porażka


>>> class adder:
...    def __init__(self, value=0):
...       self.data = value                  # Inicjalizacja zmiennej data
...    def __add__(self, other):
...       self.data += other                 # Dodanie zmiennej other w miejscu
...
>>> class addrepr(adder):                    # Odziedziczenie metod __init__ oraz __add__
...    def __repr__(self):                   # Dodanie reprezentacji łańcucha znaków
...       return 'addrepr(%s)' % self.data   # Konwersja na łańcuch znaków jako kod
...
>>> x = addrepr(2)                           # Wykonuje metodę __init__
>>> x + 1                                    # Wykonuje metodę __add__
>>> x                                        # Wykonuje metodę __repr__
addrepr(3)
>>> print x                                  # Wykonuje metodę __repr__
addrepr(3)
>>> str(x), repr(x)                          # Wykonuje metodę __repr__
('addrepr(3)', 'addrepr(3)')


>>> class addstr(adder):
...    def __str__(self):                    # __str__, ale bez __repr__
...       return '[Wartość: %s]' % self.data # Konwersja na ładny łańcuch znaków
...
>>> x = addstr(3)
>>> x + 1
>>> x                                        # Domyślnie metoda repr
<__main__.addstr instance at 0x00B35EF0>
>>> print x                                  # Wykonuje metodę __str__
[Wartość: 4]
>>> str(x), repr(x)
('[Wartość: 4]', '<__main__.addstr instance at 0x00B35EF0>')


>>> class addboth(adder):
...    def __str__(self):
...       return '[Wartość: %s]' % self.data # Łańcuch znaków przyjazny dla użytkownika
...    def __repr__(self):
...       return 'addboth(%s)' % self.data   # Łańcuch znaków jako kod
...
>>> x = addboth(4)
>>> x + 1
>>> x                                        # Wykonuje metodę __repr__
addboth(5)
>>> print x                                  # Wykonuje metodę __str__
[Wartość: 5]
>>> str(x), repr(x)
('[Wartość: 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__: instancja + nieinstancja
add 88 1
>>> 1 + y                                    # __radd__: nieinstancja + instancja
radd 99 1
>>> x + y                                    # __add__: instancja + instancja
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):                # Funkcja i informacje o stanie
      self.color = color
   def __call__(self):                       # Obsługa wywołań bez argumentów
      print 'włącz', self.color


cb1 = Callback('niebieski')                  # 'Pamięta' niebieski
cb2 = Callback('zielony')

B1 = Button(command=cb1)                     # Rejestrowanie programów obsługi
B2 = Button(command=cb2)                     # Rejestrowanie programów obsługi


cb1( )                                       # Przy zdarzeniu: wyświetla 'niebieski'
cb2( )                                       # Wyświetla 'zielony'


cb3 = (lambda color='czerwony': 'włącz ' + color) # Lub: wartości domyślne
print cb3( )


class Callback:
   def __init__(self, color):                # Klasa z informacjami o stanie
      self.color = color
   def changeColor(self):                    # Normalna nazwana metoda
      print 'włącz', self.color

cb1 = Callback('niebieski')
cb2 = Callback('zielony')

B1 = Button(command=cb1.changeColor)         # Referencja, ale bez wywołania
B2 = Button(command=cb2.changeColor)         # Pamięta funkcję i self


object = Callback('niebieski')
cb = object.changeColor                      # Zarejestrowany program obsługi zdarzeń
cb( )                                        # Po wystąpieniu zdarzenia wyświetla 'niebieski'


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


# Plik manynames.py

# -*- coding: utf-8 -*-
X = 11                                       # Globalna zmienna/atrybut z modułu (X lub manynames.X)

def f( ):
   print X                                   # Dostęp do zmiennej globalnej X (11)

def g( ):
   X = 22                                    # Zmienna lokalna (funkcji) - X ukrywa X z modułu
   print X

class C:
   X = 33                                    # Atrybut klasy (C.X)
   def m(self):
      X = 44                                 # Zmienna lokalna metody (X)
      self.X = 55                            # Atrybut instancji (instance.X)

# Ciąg dalszy pliku manynames.py

if __name__ == '__main__':
   print X                                   # 11: moduł (czyli manynames.X poza plikiem)
   f( )                                      # 11: zmienna globalna
   g( )                                      # 22: zmienna lokalna
   print X                                   # 11: zmienna modułu bez zmian

   obj = C( )                                # Utworzenie instancji
   print obj.X                               # 33: zmienna klasy odziedziczona przez instancję

   obj.m( )                                  # Dołączenie nazwy atrybutu X do instancji
   print obj.X                               # 55: instancja
   print C.X                                 # 33: klasa (czyli obj.X jeśli nie ma X w instancji)

   #print c.m.X                              # PORAŻKA: widoczna tylko w metodzie
   #print f.X                                # PORAŻKA: widoczna tylko w funkcji


# Plik otherfile.py

# -*- coding: utf-8 -*-
import manynames

X = 66
print X                                      # 66: zmienna globalna tutaj
print manynames.X                            # 11: po zaimportowaniu zmienne globalne stają się atrybutami

manynames.f( )                               # 11: X z manynames, nie zmienna globalna!
manynames.g( )                               # 22: zmienna lokalna z funkcji innego pliku

print manynames.C.X                          # 33: atrybut klasy z innego modułu
I = manynames.C( )
print I.X                                    # 33: nadal z klasy
I.m( )
print I.X                                    # 55: teraz z instancji!


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


>>> 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': 'mielonka'}

>>> X.hola( )
>>> X.__dict__
{'data1': 'mielonka', 'data2': 'jajka'}

>>> 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']
('mielonka', 'mielonka')

>>> X.data3 = 'tost'
>>> X.__dict__
{'data1': 'mielonka', 'data3': 'tost', 'data2': 'jajka'}

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


>>> X.__dict__
{'data1': 'mielonka', 'data3': 'szynka', 'data2': 'jajka'}

>>> 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']


# Plik classtree.py

# -*- coding: utf-8 -*-
def classtree(cls, indent):
   print '.'*indent, cls.__name__            # Wyświetlenie tu nazwy klasy
   for supercls in cls.__bases__:            # Rekurencja po wszystkich klasach nadrzędnych
      classtree(supercls, indent+3)          # Może odwiedzić klasę nadrzędną więcej niż raz

def instancetree(inst):
   print 'Drzewo', inst                      # Pokazanie instancji
   classtree(inst.__class__, 3)              # Przejście do jej klasy

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
Drzewo <__main__.B instance at 0x00ACB438>
... B
...... A
Drzewo <__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


# Plik person.py

# -*- coding: utf-8 -*-
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('Robert Zielony', 40)
   print bob
   print bob.lastName( )
   bob.birthDay( )
   print bob
   
   sue = Employee('Zuzanna Kleks', 44, job='programista', pay=100000)
   print sue
   print sue.lastName( )
   sue.birthDay( )
   sue.giveRaise(.10)
   print sue


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


% python person.py
<Person:
      age=40
      name=Robert Zielony
>
Zielony
<Person:
      age=41
      name=Robert Zielony
>
<Employee:
      job=programista
      pay=100000
      age=44
      name=Zuzanna Kleks
>
Kleks
<Employee:
      job=programista
      pay=110000.0
      age=46
      name=Zuzanna Kleks
>