1. Podstawy. ******************************************************************

% cat adder.py

class Adder:
    def add(self, x, y):
        print 'Nie zaimplementowano'
    def __init__(self, start=[]):
        self.data = start
    def __add__(self, other):
        return self.add(self.data, other)  # albo w klasach podrzdnych
                                           # typ zwracany?
class ListAdder(Adder):
    def add(self, x, y):
        return x +y

class DictAdder(Adder):
    def add(self, x, y):
        new = {}
        for k in x.keys(): new[k] = x[k]
        for k in y.keys(): new[k] = y[k]
        return new

% python
>>> from adder import *
>>> x = Adder()
>>> x.add(1, 2)
Nie zaimplementowano
>>> x = ListAdder()
>>> x.add([1], [2])
[1, 2]
>>> x = DictAdder()
>>> x.add({1:1}, {2:2})
{1: 1, 2: 2}

>>> x = Adder([1])
>>> x + [2]
Nie zaimplementowano
>>>
>>> x = ListAdder([1])
>>> x + [2]
[1, 2]
>>> [2] + x
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: __add__ nor __radd__ defined for these operands


2. Przecienie operatora. ****************************************************

% cat mylist.py

class MyList:
    def __init__(self, start):
        #self.wrapped = start[:]  # kopiuje pocztek: brak efektw ubocznych
        self.wrapped = []         # upewni si, czy jest tu lista
        for x in start: self.wrapped.append(x)
    def __add__(self, other):
        return MyList(self.wrapped + other)
    def __mul__(self, time):
        return MyList(self.wrapped * time)
    def __getitem__(self, offset):
        return self.wrapped[offset]
    def __len__(self):
        return len(self.wrapped)
    def __getslice__(self, low, high):
        return MyList(self.wrapped[low:high])
    def append(self, node):
        self.wrapped.append(node)
    def __getattr__(self, name):    # inni czlonkowie --sortowanie/odwracanie/itp.
        return getattr(self.wrapped, name)
    def __repr__(self):
        return `self.wrapped`

if __name__ == '__main__':
    x = MyList('mielonka')
    print x
    print x[2]
    print x[1]
    print x + ['jajka']
    print x * 3
    x.append('a')
    x.sort()
    for c in x: print c,

% python mylist.py
['m', 'i', 'e', 'l', 'o', 'n', 'k', 'a']
e
i
['m', 'i', 'e', 'l', 'o', 'n', 'k', 'a', 'jajka']
['m', 'i', 'e', 'l', 'o', 'n', 'k', 'a', 'm', 'i', 'e', 'l', 'o', 'n', 'k', 
 'a', 'm', 'i', 'e', 'l', 'o', 'n', 'k', 'a']
a a e i k l m n o


3. Klasy podrzdne. ***********************************************************

% cat mysub.py

from mylist import MyList

class MyListSub(MyList):
    calls = 0                                    # wspdzielone przez egzemplarze

    def __init__(self, start):
        self.adds = 0                            # rni si w kadym egzemplarzu
        MyList.__init__(self, start)

    def __add__(self, other):
        MyListSub.calls = MyListSub.calls + 1    # licznik szerokoci klasy
        self.adds = self.adds + 1                # zliczenia na egzemplarz
        return MyList.__add__(self, other)

    def stats(self):
        return self.calls, self.adds             # wszystkie adds, moje adds

if __name__ == '__main__':
    x = MyListSub('mielonka')
    y = MyListSub('foo')
    print x[2]
    print x[1:]
    print x + ['jajka']
    print x + ['tost']
    print y + ['bar']
    print x.stats()

% python mysub.py
e
['i', 'e', 'l', 'o', 'n', 'k', 'a']
['m', 'i', 'e', 'l', 'o', 'n', 'k', 'a', 'jajka']
['m', 'i', 'e', 'l', 'o', 'n', 'k', 'a', 'tost']
['f', 'o', 'o', 'bar']
(3, 2)


4. Metody metaklasy. **********************************************************

>>> class Meta:
...     def __getattr__(self, name):        print 'get', name
...     def __setattr__(self, name, value): print 'set', name, value
...
>>> x = Meta()
>>> x.append
get append
>>> x.mielonka = "wieprzowina"
set mielonka wieprzowina
>>>
>>> x + 2
get __coerce__
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: call of non-function
>>>
>>> x[1]
get __getitem__
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: call of non-function

>>> x[1:5]
get __len__
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: call of non-function


5. Zbiory obiektw. ***********************************************************

# naley umieci kod klasy Set z przykadw w pliku "set.py"
# w katalogu podanym w PYTHONPATH
  
% python
>>> from set import Set
>>> x = Set([1,2,3,4])          # uruchamia __init__
>>> y = Set([3,4,5])

>>> x & y                       # __and__, intersect, nastpnie __repr__
Set:[3, 4]

>>> x | y                       # __or__, union, nastpnie __repr__
Set:[1, 2, 3, 4, 5]

>>> z = Set("hello")            # __init__ usuwa duplikaty
>>> z[0], z[-1]                 # __getitem__
('h', 'o')

>>> for c in z: print c,        # __getitem__
...
h e l o
>>> len(z), z                   # __len__, __repr__
(4, Set:['h', 'e', 'l', 'o'])

>>> z & "mello", z | "mello"
(Set:['e', 'l', 'o'], Set:['h', 'e', 'l', 'o', 'm'])


# multiset.py: rozszerzenia klasy podrzdnej dla wielu operandw

from set import Set

class MultiSet(Set):
    """
    dziedziczy wszystkie nazwy z Set, ale rozszerza
    intersect i union o obsug wielu operandw;
    prosz zwrci uwag, e "self" wci jest pierwszym
    argumentem (przechowywanym teraz w argumencie *args);
    ponadto, dziedziczone operatory & i | wywouj nowe
    metody z dwoma argumentami, lecz aby obsuy wicej
    ni 2 argumenty, wymagane jest wywoanie metody, a nie wyraenie
    """

    def intersect(self, *others):
        res = []
        for x in self:                    # skanuje pierwsz sekwencj
            for other in others:          # dla wszystkich innych args
                if x not in other: break  # pozycja w kadym?
            else:                         # nie: wyjcie z ptli
                res.append(x)             # tak: dodaje pozycj na kocu
        return Set(res)

    def union(*args):                     # self w args[0]
        res = []
        for seq in args:                  # dla wszystkich args
            for x in seq:                 # dla wszystkich wzw
                if not x in res:
                    res.append(x)         # dodaje nowe pozycje do wyniku
        return Set(res)


>>> from multiset import *
>>> x = MultiSet([1,2,3,4])
>>> y = MultiSet([3,4,5])
>>> z = Multiset([0,1,2])

>>> x & y, x | y                            # 2 operandy
(Set:[3, 4], Set:[1, 2, 3, 4, 5])

>>> x.intersect(y, z)                       # 3 operandy
Set:[]
>>> x.union(y, z)
Set:[1, 2, 3, 4, 5, 0]

>>> x.intersect([1,2,3], [2,3,4], [1,2,3])  # 4 operandy
Set:[2, 3]
>>> x.union(range(10))
Set:[1, 2, 3, 4, 0, 5, 6, 7, 8, 9]


6. Dowizania drzewa klasy. ***************************************************

class Lister:
    def __repr__(self):
        return ("Instance of %s(%s), address %s:\n%s>" %
                          (self.__class__.__name__,     # nazwa mojej klasy
                           self.supers(),               # klasy nadrzdne
                           id(self),                    # mj adres
                           self.attrnames()) )          # lista name=value
    def attrnames(self):
        Nie zmienione...
    def supers(self):
        result = ""
        first = 1
        for super in self.__class__.__bases__: # jeden poziom wyej od klasy
            if not first:
                result = result + ", "
            first = 0
            result = result + super.__name__
        return result

C:\python\examples> python testmixin.py
<Instance of Sub(Super, Lister), address 7841200:
        name data3=42
        name data2=jajka
        name data1=mielonka
>


7. Kompozycja. ****************************************************************

# punkt wyjciowy...

class Lunch:
    def __init__(self)                   # utworzenie/osadzenie Klienta i Pracownika
    def zamowienie(self, jedzenieNazwa)  # start zamawiania przez Klienta
    def wynik(self)                      # zapytanie Klienta, jaki rodzaj Jedzenia chce je

class Klient:
    def __init__(self)                   # inicjacja: moje jedzenie = None
    def skladaZamowienie(self, jedzenieNazwa, pracownik)  # zamwienie
    def printJedzenie(self)              # drukowanie nazwy mojego jedzenia

class Pracownik:
    def bracZamowienie(self, jedzenieNazwa) # podaje dane Jedzenie

class Jedzenie:
    def __init__(self, nazwa)            # zachowuje nazw jedzenia


# rozwizanie...
# naley umieci poniszy kod w pliku moduowym nazwanym lunch.py
# jest on uruchamiany, a nie importowany, wic nie musi by w PYTHONPATH

class Lunch:
    def __init__(self):
        # robi/osadza Klient i Pracownik
        self.cust = Klient()
        self.empl = Pracownik()
    def zamowienie(self, jedzenieNazwa):
        # rozpoczyna symulacj zamawiania Klienta
        self.cust.skladaZamowienie(jedzenieNazwa, self.empl)
    def wynik(self):
        # pyta Klienta jaki ma rodzaj Jedzenia
        self.cust.printJedzenie()

class Klient:
    def __init__(self):
        # inicjuje moje jedzenie na None
        self.jedzenie = None
    def skladaZamowienie(self, jedzenieNazwa, pracownik):
        # skada zamowienie u Pracownik
        self.jedzenie = pracownik.bracZamowienie(jedzenieNazwa)
    def printJedzenie(self):
        # drukuje nazw mojego jedzenia
        print self.jedzenie.nazwa

class Pracownik:
    def bracZamowienie(self, jedzenieNazwa):
        # zwraca Jedzenie z dan nazw
        return Jedzenie(jedzenieNazwa)

class Jedzenie:
    def __init__(self, nazwa):
        # zapisuje nazw jedzenia
        self.nazwa = nazwa

if __name__ == '__main__':
    x = Lunch()
    x.zamowienie('burritos')
    x.wynik()
    x.zamowienie('pizza')
    x.wynik()

% python lunch.py
burritos
pizza
