# Plik setwrapper.py

# -*- coding: utf-8 -*-

class Set:
   def __init__(self, value = []):           # Konstruktor
      self.data = []                         # Zarządza listą
      self.concat(value)

   def intersect(self, other):               # other to dowolna sekwencja
      res = []                               # self to podmiot
      for x in self.data:
         if x in other:                      # Wybór wspólnych elementów
            res.append(x)
      return Set(res)                        # Zwrócenie nowej instancji Set

   def union(self, other):                   # other to dowolna sekwencja
      res = self.data[:]                     # Kopia mojej listy
      for x in other:                        # Dodanie elementów w other
         if not x in res:
            res.append(x)
      return Set(res)

   def concat(self, value):                  # value: lista, Set...
      for x in value:                        # Usuwa duplikaty
         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 'Zbiór:' + `self.data`      # Wyświetlenie za pomocą print


# Plik typesubclass.py

# -*- coding: utf-8 -*-

# Klasa podrzędna wbudowanego typu (klasy) listy.
# Odwzorowanie 1..N na 0..N-1; wywołanie z powrotem wbudowanej wersji.

class MyList(list):
   def __getitem__(self, offset):
      print '(indeksowanie %s w pozycji %s)' % (self, offset)
      return list.__getitem__(self, offset - 1)

if __name__ == '__main__':
   print list('abc')
   x = MyList('abc')                         # Metoda __init__ odziedziczona po liście
   print x                                   # Metoda __repr__ odziedziczona po liście

   print x[1]                                # MyList.__getitem__
   print x[3]                                # Dostosowuje metodę klasy nadrzędnej listy

   x.append('mielonka'); print x             # Atrybuty z klasy nadrzędnej listy
   x.reverse( ); print x


% python typesubclass.py
['a', 'b', 'c']
['a', 'b', 'c']
(indeksowanie ['a', 'b', 'c'] w pozycji 1)
a
(indeksowanie ['a', 'b', 'c'] w pozycji 3)
c
['a', 'b', 'c', 'mielonka']
['mielonka', 'c', 'b', 'a']


# Plik setsubclass.py

# -*- coding: utf-8 -*-

class Set(list):
   def __init__(self, value = []):           # Konstruktor
      list.__init__([])                      # Dostosowuje listę do własnych potrzeb
      self.concat(value)                     # Kopiuje zmienne wartości domyślne

   def intersect(self, other):               # other to dowolna sekwencja
      res = []                               # self to podmiot
      for x in self:
         if x in other:                      # Wybór wspólnych elementów
            res.append(x)
      return Set(res)                        # Zwrócenie nowej instancji Set

   def union(self, other):                   # other to dowolna sekwencja
      res = Set(self)                        # Kopia mojej listy
      res.concat(other)
      return res

   def concat(self, value):                  # value: lista, Set...
      for x in value:                        # Usuwa duplikaty
         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 'Zbiór:' + 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
Zbiór:[1, 3, 5, 7] Zbiór:[2, 1, 4, 5, 6] 4
Zbiór:[1, 5] Zbiór:[2, 1, 4, 5, 6, 3, 7]
Zbiór:[1, 5] Zbiór:[1, 3, 5, 7, 2, 4, 6]
Zbiór:[7, 5, 3, 1]   
   
   
class C1:
   def meth1(self): self.X = 88              # Zakładam, że X jest moje
   def meth2(self): print self.X
   
   
class C2:
   def metha(self): self.X = 99              # Ja też tak uważam
   def methb(self): print self.X   
   
   
class C3(C1, C2): ...
I = C3( )                                    # Tylko jedna zmienna X w I!


# Plik private.py

# -*- coding: utf-8 -*-

class C1:
   def meth1(self): self.__X = 88            # Teraz X jest moje
   def meth2(self): print self.__X           # Staje się _C1__X w I

class C2:
   def metha(self): self.__X = 99            # Jest też moje
   def methb(self): print self.__X           # Staje się _C2__X w I

class C3(C1, C2): pass
I = C3( )                                    # Dwie zmienne X w 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):
   ...normalny kod...


>>> class A: attr = 1                        # Model klasyczny
>>> class B(A): pass
>>> class C(A): attr = 2
>>> class D(B,C): pass                       # Próbuje A przed C
>>> x = D( )
>>> x.attr
1


>>> class A(object): attr = 1                # Nowy styl klas
>>> class B(A): pass
>>> class C(A): attr = 2
>>> class D(B,C): pass                       # Próbuje C przed A
>>> x = D( )
>>> x.attr
2


>>> class A: attr = 1                        # Model klasyczny
>>> class B(A): pass
>>> class C(A): attr = 2
>>> class D(B,C): attr = C.attr              # Wybór C, na prawo
>>> x = D( )
>>> x.attr                                   # Działa jak nowy styl
2


>>> class A(object): attr = 1                # Nowy styl
>>> class B(A): pass
>>> class C(A): attr = 2
>>> class D(B,C): attr = B.attr              # Wybór A.attr, z góry
>>> x = D( )
>>> x.attr                                   # Działa jak model klasyczny
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                       # Wykorzystanie domyślnej kolejności wyszukiwania
>>> x = D( )                                 # Różni się dla typów klas
>>> x.meth( )                                # Domyślnie kolejność klasyczna
A.meth

>>> class D(B,C): meth = C.meth              # Wybór metody klasy C: nowy styl
>>> x = D( )
>>> x.meth( )
C.meth

>>> class D(B,C): meth = B.meth              # Wybór metody klasy B: model klasyczny
>>> x = D( )
>>> x.meth( )
A.meth


class D(B,C):
   def meth(self):                           # Redefiniowanie niżej
      ...
      C.meth(self)                           # Wybór metody klasy C przez wywołanie


>>> class limiter(object):
...    __slots__ = ['age', 'name', 'job']
...
>>> x = limiter( )
>>> x.age                                    # Trzeba przypisać przed użyciem
AttributeError: age

>>> x.age = 40
>>> x.age
40
>>> x.ape = 1000                             # Niedozwolony: nie występuje w __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                                    # Wykonuje __getattr__
40
>>> x.name                                   # Wykonuje __getattr__
AttributeError


>>> class newprops(object):
...    def getage(self):
...       return 40
...    age = property(getage, None, None, None) # Operacje get, set, del, dokumentacja
...
>>> x = newprops( )
>>> x.age                                    # Wykonuje getage
40
>>> x.name                                   # Normalne pobranie
AttributeError: newprops instance has no attribute 'name'


>>> class newprops(object):
...    def getage(self):
...       return 40
...    def setage(self, value):
...       print 'ustawienie wieku:', value
...       self._age = value
...    age = property(getage, setage, None, None)
...
>>> x = newprops( )
>>> x.age                                    # Wykonuje getage
40
>>> x.age = 42                               # Wykonuje setage
ustawienie wieku: 42
>>> x._age                                   # Normalne pobranie; nie ma wywołania getage
42
>>> x.job = 'instruktor'                     # Normalne przypisanie; nie ma wywołania setage
>>> x.job                                    # Normalne pobranie; nie ma wywołania getage
'instruktor'


>>> class classic:
...    def __getattr__(self, name):          # Przy niezdefiniowanej referencji
...       if name == 'age':
...          return 40
...       else:
...          raise AttributeError
...    def __setattr__(self, name, value):   # Przy wszystkich przypisaniach
...       print 'ustawienie:', name, value
...       if name == 'age':
...          self.__dict__['_age'] = value
...       else:
...          self.__dict__[name] = value
...
>>> x = classic( )
>>> x.age                                    # Wykonuje __getattr__
40
>>> x.age = 41                               # Wykonuje __setattr__
ustawienie: age 41
>>> x._age                                   # Zdefiniowane: nie ma wywołania __getattr__
41
>>> x.job = 'instruktor'                     # Wykonuje znowu __setattr__
>>> x.job                                    # Zdefiniowane: nie ma wywołania __getattr__


# Plik spam.py

# -*- coding: utf-8 -*-

class Spam:
   numInstances = 0
   def __init__(self):
      Spam.numInstances = Spam.numInstances + 1
   def printNumInstances( ):
      print "Liczba utworzonych instancji: ", 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 "Liczba utworzonych instancji: ", 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( )
Liczba utworzonych instancji: 3
>>> spam.Spam.numInstances
3


class Spam:
   numInstances = 0
   def __init__(self):
      Spam.numInstances = Spam.numInstances + 1
   def printNumInstances(self):
      print "Liczba utworzonych instancji: ", Spam.numInstances

>>> from spam import Spam
>>> a, b, c = Spam(), Spam( ), Spam( )
>>> a.printNumInstances( )
Liczba utworzonych instancji: 3
>>> b.printNumInstances( )
Liczba utworzonych instancji: 3
>>> Spam( ).printNumInstances( )
Liczba utworzonych instancji: 4


class Multi:
   def imeth(self, x):                       # Normalna metoda instancji
      print self, x
   def smeth(x):                             # Metoda statyczna: nie przekazano instancji
      print x
   def cmeth(cls, x):                        # Metoda klasy: otrzymuje klasę, nie instancję
      print cls, x
   smeth = staticmethod(smeth)               # smeth staje się metodą statyczną
   cmeth = classmethod(cmeth)                # cmeth staje się metodą klasy


>>> obj = Multi( )                           # Utworzenie instancji
>>> obj.imeth(1)                             # Normalne wywołanie, przez instancję
<__main__.Multi instance...> 1
>>> Multi.imeth(obj, 2)                      # Normalne wywołanie, przez klasę
<__main__.Multi instance...> 2


>>> Multi.smeth(3)                           # Wywołanie statyczne, przez klasę
3
>>> obj.smeth(4)                             # Wywołanie statyczne, przez instancję
4


>>> Multi.cmeth(5)                           # Wywołanie klasy, przez klasę
__main__.Multi 5
>>> obj.cmeth(6)                             # Wywołanie klasy, przez instancję
__main__.Multi 6


# Plik spam2.py

class Spam:
   numInstances = 0
   def __init__(self):
      Spam.numInstances += 1
   def printNumInstances( ):
      print "Liczba utworzonych instancji:", Spam.numInstances
   printNumInstances = staticmethod(printNumInstances)

>>> a = Spam( )
>>> b = Spam( )
>>> c = Spam( )
>>> Spam.printNumInstances( )
Liczba utworzonych instancji: 3
>>> a.printNumInstances( )
Liczba utworzonych instancji: 3


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


class C:
   def meth( ):
      ...
   meth = staticmethod(meth)                 # Ponowne wiązanie nazwy


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


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


# Plik decorator.py

# -*- coding: utf-8 -*-
class tracer:
   def __init__(self, func):
      self.calls = 0
      self.func = func
   def __call__(self, *args):
      self.calls += 1
      print 'wywołanie %s do %s' % (self.calls, self.func._ _name_ _)
      self.func(*args)

@tracer
def spam(a, b, c):                           # Opakowanie funkcji spam w obiekt dekoratora
   print a, b, c

spam(1, 2, 3)                                # Naprawdę wywołuje obiekt opakowujący tracer
spam('a', 'b', 'c')                          # Wywołuje __call__ w klasie
spam(4, 5, 6)                                # __call__ dodaje logikę i wykonuje oryginalny obiekt


wywołanie 1 do spam
1 2 3
wywołanie 2 do spam
a b c
wywołanie 3 do spam
4 5 6


>>> class X:
...   a = 1                                  # Atrybut klasy
...
>>> I = X( )
>>> I.a                                      # Odziedziczony przez instancję
1
>>> X.a
1


>>> X.a = 2                                  # Może zmienić coś więcej niż tylko X
>>> I.a                                      # I również się zmienia
2
>>> J = X( )                                 # J dziedziczy po X wartości w czasie wykonywania
>>> J.a                                      # (przypisanie do J.a zmienia a w J, a nie w X lub I)
2


class X: pass                                # Utworzenie kilku przestrzeni nazw atrybutów
class Y: pass

X.a = 1                                      # Wykorzystanie atrybutów klas jako zmiennych
X.b = 2                                      # Nigdzie nie ma instancji
X.c = 3
Y.a = X.a + X.b + X.c

for X.i in range(Y.a): print X.i             # Wyświetla 0..5


class Record: pass
X = Record( )
X.name = 'robert'
X.job = 'Twórca pizzy'


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

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

class Sub(Lister, Super):                    # Pobranie __repr__ z Lister dzięki podaniu jej jako pierwszej


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

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

class Sub(Lister, Super):                    # Pobranie __repr__ z Lister dzięki podaniu jej jako pierwszej
   other = Super.other                       # W jawny sposób wybranie wersji other z klasy Super
   def __init__(self):
      ...

x = Sub( )                                   # Dziedziczenie przeszukuje Sub przed Super/Lister


class Sub(Super, Lister):                    # Pobranie other z klasy Super dzięki kolejności
   __repr__ = Lister.__repr__                # Jawny wybór Lister.__repr__


# Plik nester.py

# -*- coding: utf-8 -*-
def generate( ):
   class Spam:
      count = 1
      def method(self):                      # Nazwa Spam nie jest widoczna:
         print Spam.count                    # Nie lokalna (def), globalna (moduł), wbudowana
   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                       # Nie lokalna (def), globalna (moduł), wbudowana
NameError: Spam


def generate( ):
   global Spam                               # Zmuszenie Spam do istnienia w zakresie modułu
   class Spam:
      count = 1
      def method(self):
         print Spam.count                    # Działa: w zakresie globalnym (modułu)
   return Spam( )

generate( ).method( )                        # Wyświetla 1


def generate( ):
   return Spam( )

class Spam:                                  # Zdefiniowana na najwyższym poziomie modułu
   count = 1
   def method(self):
      print Spam.count                       # Działa: w zakresie globalnym (modułu)

generate( ).method( )


def generate( ):
   class Spam:
      count = 1
      def method(self):
         print self.__class__.count          # Działa: kwalifikacja, by dotrzeć do klasy
   return Spam( )

generate( ).method( )


<Instancja klasy Sub(Super, Lister), adres 7841200:


class Lunch:
   def __init__(self)                        # Tworzy/osadza klasy Customer oraz Employee
   def order(self, foodName)                 # Rozpoczęcie symulacji zamówienia klienta Customer
   def result(self)                          # Zapytanie klienta Customer, jakie ma jedzenie Food 

class Customer:
   def __init__(self)                        # Inicjalizacja mojego jedzenia na None
   def placeOrder(self, foodName, employee)  # Złożenie zamówienia pracownikowi Employee
   def printFood(self)                       # Wyświetlenie nazwy mojego jedzenia

class Employee:
   def takeOrder(self, foodName)             # Zwraca jedzenie Food z żądaną nazwą

class Food:
   def __init__(self, name)                  # Przechowanie nazwy jedzenia


% python
>>> from zoo import Cat, Hacker
>>> spot = Cat( )
>>> spot.reply( )                            # Animal.reply; wywołuje Cat.speak
miau
>>> data = Hacker( )                         # Animal.reply; wywołuje Primate.speak
>>> data.reply( )
Witaj, świecie!


% python
>>> import parrot
>>> parrot.Scene( ).action( )                # Aktywacja zagnieżdżonych obiektów
customer: "To już ekspapuga!"
clerk: "nie, wcale nie..."
parrot: None