﻿class C:
    def meth(self, x):
        ...
    def meth(self, x, y, z):
        ...


class C:
    def meth(self, *args):
        if len(args) == 1:
            ...
        elif type(arg[0]) == int:
            ...


class C:
    def meth(self, x):
        x.operation()                         # Zakładamy, że x robi coś właściwego


### plik: employees.py

class Employee:
    def __init__(self, name, salary=0):
        self.name = name
        self.salary = salary
    def giveRaise(self, percent):
        self.salary = self.salary + (self.salary * percent)
    def work(self):
        print self.name, "robi różne rzeczy"
    def __repr__(self):
       return "<Pracownik: imię=%s, wynagrodzenie=%s>" % (self.name, self.salary)

class Chef(Employee):
    def __init__(self, name):
        Employee.__init__(self, name, 50000)
    def work(self):
        print self.name, "przygotowuje jedzenie"

class Server(Employee):
    def __init__(self, name):
        Employee.__init__(self, name, 40000)
    def work(self):
        print self.name, "obsługuje klienta"

class PizzaRobot(Chef):
    def __init__(self, name):
        Chef.__init__(self, name)
    def work(self):
        print self.name, "przygotowuje pizzę"

if __name__ == "__main__":
    bob = PizzaRobot('robert')                # Tworzy robota o imieniu Robert
    print bob                                 # Wykonuje odziedziczoną metodę __repr__
    bob.work()                                # Wykonuje działanie specyficzne dla typu
    bob.giveRaise(0.20)                       # Daje robotowi 20-procentową podwyżkę
    print bob; print

    for klass in Employee, Chef, Server, PizzaRobot:
        obj = klass(klass.__name__)
        obj.work()


C:\python\examples> python employees.py

### plik: pizzashop.py

from employees import PizzaRobot, Server

class Customer:
    def __init__(self, name):
        self.name = name
    def order(self, server):
        print self.name, "zamawia od", server
    def pay(self, server):
        print self.name, "płaci za zamówienie", server

class Oven:
    def bake(self):
        print "piec piecze"

class PizzaShop:
    def __init__(self):
        self.server = Server('Ernest')         # Osadzenie innych obiektów
        self.chef = PizzaRobot('Robert')       # Robot o imieniu Robert
        self.oven = Oven()

    def order(self, name):
        customer = Customer(name)              # Aktywacja innych obiektów
        customer.order(self.server)            # Klient zamawia od kelnera
        self.chef.work()
        self.oven.bake()
        customer.pay(self.server)

if __name__ == "__main__":
    scene = PizzaShop()                       # Utworzenie kompozytu
    scene.order('Amadeusz')                   # Symulacja zamówienia Amadeusza
    print '...'
    scene.order('Aleksander')                 # Symulacja zamówienia Aleksandra


C:\python\examples> python pizzashop.py


def processor(reader, converter, writer):
    while 1:
        data = reader.read()
        if not data: break
        data = converter(data)
        writer.write(data)


### plik: streams.py

class Processor:
    def __init__(self, reader, writer):
        self.reader = reader
        self.writer = writer
    def process(self):
        while 1:
            data = self.reader.readline()
            if not data: break
            data = self.converter(data)
            self.writer.write(data)
    def converter(self, data):
        assert 0, 'konwerter musi być zdefiniowany' # lub wywołać wyjątek


### plik: converters.py

from streams import Processor

class Uppercase(Processor):
    def converter(self, data):
        return data.upper()

if __name__ == '__main__':
    import sys
    obj = Uppercase(open('spam.txt'), sys.stdout)
    obj.process()


C:\lp4e> type spam.txt
mielonka
Mielonka
MIELONKA!

C:\lp4e> python converters.py
MIELONKA
MIELONKA
MIELONKA!


C:\lp4e> python
>>> import converters
>>> prog = converters.Uppercase(open('spam.txt'), open('spamup.txt', 'w'))
>>> prog.process()

C:\lp4e> type spamup.txt
MIELONKA
MIELONKA
MIELONKA!


C:\lp4e> python
>>> from converters import Uppercase
>>>
>>> class HTMLize:
...    def write(self, line):
...       print '<PRE>%s</PRE>' % line[:-1]
...
>>> Uppercase(open('spam.txt'), HTMLize()).process()
<PRE>MIELONKA</PRE>
<PRE>MIELONKA</PRE>
<PRE>MIELONKA!</PRE>


import pickle
object = someClass()
file = open(filename, 'wb')                  # Utworzenie pliku zewnętrznego
pickle.dump(object, file)                    # Zapisanie obiektu w pliku

import pickle
file = open(filename, 'rb')
object = pickle.load(file)                   # Pobranie go z powrotem później


import shelve
object = someClass()
dbase = shelve.open('filename')
dbase['key'] = object                        # Zapisanie pod kluczem

import shelve
dbase = shelve.open('filename')
object = dbase['key']                        # Pobranie z powrotem później


>>> from pizzashop import PizzaShop
>>> shop = PizzaShop()
>>> shop.server, shop.chef
(<Pracownik: imię=Ernest, wynagrodzenie=40000>, <Pracownik: imię=Robert, 
wynagrodzenie=50000>)
>>> import pickle
>>> pickle.dump(shop, open('shopfile.dat', 'wb'))


>>> import pickle
>>> obj = pickle.load(open('shopfile.dat', 'rb'))
>>> obj.server, obj.chef
(<Employee: name=Pat, salary=40000>, <Employee: name=Bob, salary=50000>)
>>> obj.order('Zuzanna')
Zuzanna zamawia od <Pracownik: imię=Ernest, wynagrodzenie=40000>
Robert przygotowuje pizzę
piec piecze
Zuzanna płaci za zamówienie <Pracownik: imię=Ernest, wynagrodzenie=40000>


### plik: trace.py

class wrapper:
    def __init__(self, object):
        self.wrapped = object                  # Zapisanie obiektu
    def __getattr__(self, attrname):
        print 'Śledzenie:', attrname           # Śledzenie pobrania
        return getattr(self.wrapped, attrname) # Delegacja pobrania


>>> from trace import wrapper
>>> x = wrapper([1,2,3])                     # Opakowanie listy
>>> x.append(4)                              # Delegacja do metody listy
Śledzenie: append
>>> x.wrapped                                # Wyświetlenie mojej składowej
[1, 2, 3, 4]

>>> x = wrapper({"a": 1, "b": 2})            # Opakowanie słownika
>>> list(x.keys())                           # Delegacja do metody słownika
Śledzenie: keys
['a', 'b']


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

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 Super:
    def method(self): ...                   # Zwykła metoda

class Tool:
    def __method(self): ...                 # Nazwa zostanie niejawnie zmieniona na _Tool__method
    def other(self): self.__method()        # Użycie metody wewnętrznej

class Sub1(Tool, Super): ...
    def actions(self): self.method()        # Wywołuje Super.method()

class Sub2(Tool):
def __init__(self): self.method = 99        # Nie psuje Tool.__method


class Spam:
    def doit(self, message):
        print message


object1 = Spam()
object1.doit('Witaj, świecie!')


object1 = Spam()
x = object1.doit                             # Obiekt metody z wiązaniem: instancja + funkcja
x('Witaj, świecie!')                         # Ten sam efekt co object1.doit('...')


object1 = Spam()
t = Spam.doit                                # Obiekt metody bez wiązania
t(object1, 'siema')                          # Przekazanie instancji


class Eggs:
    def m1(self, n):
        print n
    def m2(self):
        x = self.m1                            # Inny obiekt metody z wiązaniem
        x(42)                                  # Wygląda jak prosta funkcja

Eggs().m2()                                    # Wyświetla 42


C:\misc> c:\python30\python
>>> class Selfless:
...     def __init__(self, data):
...         self.data = data
...     def selfless(arg1, arg2):           # w 3.0 jest zwykłą funkcją
...         return arg1 + arg2
...     def normal(self, arg1, arg2):       # oczekuje instancji
...         return self.data + arg1 + arg2
...
>>> X = Selfless(2)
>>> X.normal(3, 4)                          # instancja jest przekazana automatycznie
9
>>> Selfless.normal(X, 3, 4)                # parametr self oczekiwany przez metodę przekazany ręcznie
9
>>> Selfless.selfless(3, 4)                 # brak instancji: działa w 3.0, ale nie w 2.6!
7


>>> X.selfless(3, 4)
TypeError: selfless() takes exactly 2 positional arguments (3 given)
>>> Selfless.normal(3, 4)
TypeError: normal() takes exactly 3 positional arguments (2 given)


>>> class Number:
...     def __init__(self, base):
...         self.base = base
...     def double(self):
...         return self.base * 2
...     def triple(self):
...         return self.base * 3
...
>>> x = Number(2)                                   # Obiekty klasy
>>> y = Number(3)                                   # Stan i metody
>>> z = Number(4)
>>> x.double()                                      # Zwykłe wywołania bezpośrednie
4

>>> acts = [x.double, y.double, y.triple, z.double] # Lista metod związanych
>>> for act in acts:                                # Opóźnienie wywołań
...   print(act())                                  # Wywołanie w trybie zwykłych funkcji
...
4
6
9
8


>>> bound = x.double
>>> bound.__self__, bound.__func__
(<__main__.Number object at 0x0278F610>, <function double at 0x027A4ED0>)
>>> bound.__self__.base
2
>>> bound()                                 # Wywołuje bound.__func__(bound.__self__, ...)
4


>>> def square(arg):
...     return arg ** 2                         # Zwykłe funkcje (def lub lambda)
...
>>> class Sum:
...     def __init__(self, val):                # Klasa wywoływana
...         self.val = val
...     def __call__(self, arg):
...         return self.val + arg
...
>>> class Product:
...     def __init__(self, val):                # Metody związane
...         self.val = val
...     def method(self, arg):
...         return self.val * arg
...
>>> sobject = Sum(2)
>>> pobject = Product(3)
>>> actions = [square, sobject, pobject.method] # Funkcja, instancja, metoda

>>> for act in actions:                         # wszystkie z nich wywołuje się tak samo
...     print(act(5))                           # jako obiekt wywoływany z jednym argumentem
...
25
7
15
>>> actions[-1](5)                              # Indeksowanie, składanie, mapy
15
>>> [act(5) for act in actions]
[25, 7, 15]
>>> list(map(lambda act: act(5), actions))
[25, 7, 15]


>>> class Negate:
...     def __init__(self, val):                      # Klasy też są obiektami wywoływanymi
...         self.val = -val                           # Wywołanie z poziomu obiektu nie zadziała
...     def __repr__(self):                           # Format reprezentacji tekstowej
...         return str(self.val)
...
>>> actions = [square, sobject, pobject.method, Negate] # Wywołanie klasy
>>> for act in actions:
...     print(act(5))
...
25
7
15
-5
>>> [act(5) for act in actions]                       # Wywołuje __repr__(), nie __str__()!
[25, 7, 15, -5]

>>> table = {act(5): act for act in actions}          # Składanie słownika z 2.6/3.0
>>> for (key, value) in table.items():
...     print('{0:2} => {1}'.format(key, value))      # Formatowanie ciągu znaków w 2.6/3.0
...
-5 => <class '__main__.Negate'> 
25 => <function square at 0x025D4978>
15 => <bound method Product.method of <__main__.Product object at 0x025D0F90>>
7 => <__main__.Sum object at 0x025D0F70>


def handler():
   ...wykorzystanie zmiennych globalnych dla stanu...
...
widget = Button(text='mielonka', command=handler)


class MyWidget:
    def handler(self):
        ...użycie self.attr dla stanu...
    def makewidgets(self):
        b = Button(text='mielonka', command=self.handler)


>>> class Spam:
...    def __init__(self):                   # Nie ma __repr__
...       self.data1 = "jedzenie"
...
>>> X = Spam()
>>> print X                                  # Wygląd domyślny: klasa, adres
<__main__.Spam instance at 0x00864818>>      # W Pythonie 2.6 wyświetla "instance"


# Plik lister.py

class ListInstance:
    """
    Klasa ListInstance obsługuje formatowanie ciągów znaków zwracanych
    z funkcji print() i str(). Mechanizm jest definiowany w metodzie specjalnej
    __str__(), w naszym przypadku wypisuje nazwy i wartości atrybutów.
    Argument self jest instancją klasy na najniższym poziomie dziedziczenia.
    W przykładzie wykorzystujemy atrybuty pseudoprywatne __X w celu
    uniknięcia konfliktów nazw z atrybutami klas z drzewa dziedziczenia.
    """
    def __str__(self):
        return '<Instancja klasy %s, adres %s:\n%s>' % (
                              self.__class__.__name__,     # nazwa klasy
                              id(self),                    # adres
                              self.__attrnames())          # lista nazwa=wartość
    def __attrnames(self):
        result = ''
        for attr in sorted(self.__dict__):                 # słownik atrybutów instancji
            result += '\tnazwa %s=%s\n' % (attr, self.__dict__ [attr])
        return result


>>> from lister import ListInstance
>>> class Spam(ListInstance):               # Dziedziczy metodę __str__
...     def __init__(self):
...         self.data1 = 'food'
...
>>> x = Spam()
>>> print(x)                                # print() i str() wywołują __str__
<Instancja klasy Spam, adres 18931664:
        nazwa data1=food
>


>>> str(x)
'<Instancja klasy Spam, adres 18931664:\n\tname data1=food\n>'
>>> x                                       # Nadal domyślna jest metoda __repr__
<__main__.Spam instance at 0x0120DFD0>


# Plik testmixin.py

from lister import *                        # Import klas modułu lister

class Super:
    def __init__(self):
        self.data1 = 'spam'                 # Tworzenie atrybutów instancji
    def ham(self):
        pass

class Sub(Super, ListInstance):             # Wmieszanie metod ham() i __str__()
    def __init__(self):
        Super.__init__(self)
        self.data2 = 'eggs'                 # Więcej atrybutów instancji
        self.data3 = 42
    def spam(self):                         # Definiujemy jeszcze jedną metodę
        pass

if __name__ == '__main__':
    X = Sub()
    print(X)                                # Wywołuje wmieszaną metodę __str__()


C:\misc> C:\python30\python testmixin.py
<Instancja klasy Sub, adres 18922552:
        nazwa data1=spam
        nazwa data2=eggs
        nazwa data3=42
>


>>> import lister
>>> class C(lister.ListInstance): pass
...
>>> x = C()
>>> x.a = 1; x.b = 2; x.c = 3
>>> print(x)
<Instancja klasy C, adres 18922232:
        nazwa a=1
        nazwa b=2
        nazwa c=3
>


# Plik lister.py, ciąg dalszy

class ListInherited:
    """
    Wykorzystujemy funkcję dir() do uzyskania listy atrybutów instancji
    oraz atrybutów odziedziczonych. W Pythonie 3.0 uzyskamy większą liczbę
    nazw w porównaniu z 2.6 z powodu różnic w drzewie dziedziczenia w nowym
    modelu klas. Do uzyskania dostępu do atrybutów niedostępnych w słowniku
    __dict__ należy posłużyć się funkcją getattr(). Nie należy w ten sposób
    przesłaniać metody __repr__(), ponieważ spowoduje to zapętlenie przy
    próbie wypisania metod związanych!
    """
    def __str__(self):
        return '<Instancja klasy %s, adres %s:\n%s>' % (
                              self.__class__.__name__,     # nazwa klasy
                              id(self),                    # adres
                              self.__attrnames())          # lista nazwa=wartość
    def __attrnames(self):
        result = ''
        for attr in dir(self):                             # Lista nazw atrybutów
            if attr[:2] == '__' and attr[-2:] == '__':     # Pomijamy nazwy wewnętrzne
                result += '\tname %s=<>\n' % attr
            else:
                result += '\tname %s=%s\n' % (attr, getattr(self, attr))
        return result


### Plik: testmixin.py (modyfikacja)

class Sub(Super, ListInherited):            # Wmieszana nazwa __str__


C:\misc> c:\python26\python testmixin.py


C:\misc> c:\python30\python testmixin.py



# Plik lister.py, ciąg dalszy

class ListTree:
    """
    Klasa mieszana definiująca metodę __str__() służącą do zwracania
    tekstowej reprezentacji instancji w postaci drzewa dziedziczenia
    i atrybutów definiowanych na każdym poziomie. Wywołanie tej metody jest
    automatyczne w efekcie wywołania na instancji funkcji print() lub
    str(). Klasa wykorzystuje nazwy pseudoprywatne __X w celu uniknięcia
    konfliktu nazw w klasach potomnych. Do rekurencyjnego przeglądania
    klas nadrzędnych wykorzystuje wyrażenia generatorów oraz str.format()
    do czytelnego formatowania wyników.
    """
    def __str__(self):
        self.__visited = {}
        return '<Instancja klasy {0}, adres {1}:\n{2}{3}>'.format(
                                            self.__class__.__name__,
                                            id(self),
                                            self.__attrnames(self, 0),
                                            self.__listclass(self.__class__, 4))

    def __listclass(self, aClass, indent):
        dots = '.' * indent
        if aClass in self.__visited:
            return '\n{0}<Klasa {1}:, adres {2}: (patrz wyżej)>\n'.format(
                           dots,
                           aClass.__name__,
                           id(aClass))
        else:
            self.__visited[aClass] = True
            genabove = (self.__listclass(c, indent+4) for c in aClass.__bases__)
            return '\n{0}<Klas {1}, adres {2}:\n{3}{4}{5}>\n'.format(
                           dots,
                           aClass.__name__,
                           id(aClass),
                           self.__attrnames(aClass, indent),
                           ''.join(genabove),
                           dots)

    def __attrnames(self, obj, indent):
        spaces = ' ' * (indent + 4)
        result = ''
        for attr in sorted(obj.__dict__):
            if attr.startswith('__') and attr.endswith('__'):
                result += spaces + '{0}=<>\n'.format(attr)
            else:
                result += spaces + '{0}={1}\n'.format(attr, getattr(obj, attr))
        return result


### Plik: testmixin.py (modyfikacja)

class Sub(Super, ListTree):      # Wmieszana metoda __str__


C:\misc> c:\python26\python testmixin.py

C:\misc> c:\python30\python testmixin.py


>>> from lister import ListTree
>>> from tkinter import Button              # Obie klasy definiują metodę __str__()
>>> class MyButton(ListTree, Button): pass  # Najpierw ListTree: używamy __str__()
...
>>> B = MyButton(text='mielonka')
>>> open('savetree.txt', 'w').write(str(B)) # Zapis wyniku do pliku
18247
>>> print(B)                                # Wypisanie wyniku
<Instancja klasy MyButton, adres 23423696:
    _ListTree__visited={}
    _name=23423696
    _tclCommands=[]
    ...pominięty duży fragment wyniku...
>


def factory(aClass, *args):                  # Krotka argumentów o zmiennej liczbie
   return aClass(*args)                      # Wywołanie konstruktora klasy aClass l (lub apply w 2.6)

class Spam:
   def doit(self, message):
      print message

class Person:
   def __init__(self, name, job):
      self.name = name
      self.job = job
  
object1 = factory(Spam)                      # Utworzenie obiektu Spam
object2 = factory(Person, "Guido", "guru")   # Utworzenie obiektu Person


def factory(aClass, *args, **kwargs):        # + słownik argumentów ze słowami kluczowymi
   return apply(aClass, args, kwargs)        # Wywołanie aClass


classname = ...odczytane z pliku konfiguracyjnego...
classarg = ...odczytane z pliku konfiguracyjnego...

import streamtypes                           # Kod można dostosować do własnych potrzeb
aclass = getattr(streamtypes, classname)     # Pobrane z modułu
reader = factory(aclass, classarg)           # Lub aclass(classarg)
processor(reader, ...)   
