﻿### plik: number.py

class Number:
    def __init__(self, start):                  # Wywoływana przy Number(start)
        self.data = start
    def __sub__(self, other):                   # Wywoływana przy instancja – inna
        return Number(self.data - other)        # Wynik jest nową instancją

>>> from number import Number                   # Załadowanie klasy z modułu
>>> X = Number(5)                               # Number.__init__(X, 5)
>>> Y = X – 2                                   # Number.__sub__(X, 2)
>>> Y.data                                      # Y jest nową instancją klasy Number
3


>>> class Indexer:
...     def __getitem__(self, index):
...         return index ** 2
...
>>> X = Indexer()
>>> X[2]                                # X[i] wywołuje X.__getitem__(i)
4

>>> for i in range(5):
...     print(X[i], end=' ')            # Przy każdej iteracji wywołuje __getitem__(X, i)
...
0 1 4 9 16


>>> L = [5, 6, 7, 8, 9]
>>> L[2:4]                              # Wycinanie z użyciem składni wycinków
[7, 8]
>>> L[1:]
[6, 7, 8, 9]
>>> L[:-1]
[5, 6, 7, 8]
>>> L[::2]
[5, 7, 9]


>>> L[slice(2, 4)]                      # Wycinanie z użyciem obiektów
[7, 8]
>>> L[slice(1, None)]
[6, 7, 8, 9]
>>> L[slice(None, −1)]
[5, 6, 7, 8]
>>> L[slice(None, None, 2)]
[5, 7, 9]


>>> class Indexer:
...     data = [5, 6, 7, 8, 9]
...     def __getitem__(self, index):   # Wywoływany przy indeksowaniu i wycinaniu
...         print('getitem:', index)
...         return self.data[index]     # Realizuje dostęp po indeksie lub wycinanie
...
>>> X = Indexer()
>>> X[0]                                # Indeksowanie wysyła metodzie __getitem__ liczbę całkowitą
getitem: 0
5
>>> X[1]
getitem: 1
6
>>> X[-1]
getitem: -1
9


>>> X[2:4]                              # Wycinanie wysyła metodzie __getitem__ obiekt wycinka
getitem: slice(2, 4, None)
[7, 8]
>>> X[1:]
getitem: slice(1, None, None)
[6, 7, 8, 9]
>>> X[:-1]
getitem: slice(None, -1, None)
[5, 6, 7, 8]
>>> X[::2]
getitem: slice(None, None, 2)
[5, 7, 9]


def __setitem__(self, index, value):    # Przechwytuje przypisania do indeksu lub wycinka
    ...
    self.data[index] = value            # Przypisanie do indeksu lub wycinka


>>> class stepper:
...     def __getitem__(self, i):
...         return self.data[i]
...
>>> X = stepper()                     # X jest instancją klasy stepper


>>> class C:
...     def __index__(self):
...         return 255
...
>>> X = C()
>>> hex(X)               # Wartość całkowita
'0xff'
>>> bin(X)
'0b11111111'
>>> oct(X)
'0o377'


>>> ('C' * 256)[255]
'C'
>>> ('C' * 256)[X]       # Indeks po X (nie X[i])
'C'
>>> ('C' * 256)[X:]      # Indeks po X (nie X[i:])
'C'


>>> X.data = "Mielonka"
>>>
>>> X[1]                              # Indeksowanie wywołuje __getitem__
'p'
>>> for item in X:                    # Pętle for wywołują __getitem__
...     print(item, end=' ')          # for indeksuje 0..N
...
M i e l o n k a


>>> 'p' in X                          # Wszystkie przykłady wywołują __getitem__
True

>>> [c for c in X]                    # Lista składana
['S', 'p', 'a', 'm']

>>> list(map(str.upper, X))           # Funkcja (w 3.0 niejawnie wywołuje list())
['S', 'P', 'A', 'M']

>>> (a, b, c, d) = X                  # Przypisania sekwencji
>>> a, c, d
('S', 'a', 'm')

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

>>> X
<__main__.stepper object 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:            # Również wywoływane przez funkcję wbudowaną next
         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, end='')                     # Każda iteracja wywołuje metodę next()
...
1 4 9 16 25


>>> X = Squares(1, 5)                   # Ręczna iteracja: tak działają pętle
>>> I = iter(X)                         # iter wywołuje __iter__
>>> next(I)                             # next wywołuje __next__
1
>>> next(I)
4
...pominięta część wyniku...
>>> next(I)
25
>>> next(I)                             # Można zastosować w instrukcji try:
StopIteration


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


>>> def gsquares(start, stop):
...     for i in range(start, stop+1):
...         yield i ** 2
...
>>> for i in gsquares(1, 5):                  # lub: (x ** 2 for x in range(1, 5))
...     print(i, end=' ')
...
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

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(next(I), next(I), next(I))          # 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, end=' ')               # 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 Iters:
    def __init__(self, value):
        self.data = value
    def __getitem__(self, i):               # Metoda zastępcza do użycia przez iterację
        print('get[%s]:' % i, end='')       # oraz do indeksowania i wycinania
        return self.data[i]
    def __iter__(self):                     # Metoda preferowana w iteracji
        print('iter=> ', end='')            # Pozwala na użycie tylko jednego iteratora
        self.ix = 0
        return self
    def __next__(self):
        print('next:', end='')
        if self.ix == len(self.data): raise StopIteration
        item = self.data[self.ix]
        self.ix += 1
        return item
    def __contains__(self, x):              # Metoda preferowana w operacji 'in'
        print('contains: ', end='')
        return x in self.data

X = Iters([1, 2, 3, 4, 5])                  # Utworzenie instancji
print(3 in X)                               # Przynależność
for i in X:                                 # Pętle for
    print(i, end=' | ')

print()
print([i ** 2 for i in X])                  # Inne konteksty iteracyjne
print(list(map(bin, X)))

I = iter(X)                                 # Ręczna iteracja (demonstracja mechanizmu stosowanego 
                                            # w kontekstach iteracyjnych)
while True:
    try:
        print(next(I), end=' @ ')
    except StopIteration:
        break


>>> X = Iters('spam')               # Indeksowanie
>>> X[0]                            # __getitem__(0)
get[0]:'s'

>>> 'spam'[1:]                      # Składnia wycinania
'pam'
>>> 'spam'[slice(1, None)]          # Obiekt wycinka
'pam'

>>> X[1:]                           # __getitem__(slice(..))
get[slice(1, None, None)]:'pam'
>>> X[:-1]
get[slice(None, -1, None)]:'spa'


>>> 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
...
>>> x = adder()                              # Domyślne wyświetlanie
>>> print(x)
<__main__.adder object at 0x025D66B0>
>>> x
<__main__.adder object at 0x025D66B0>


>>> 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)                          # W obydwu przypadkach 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 Printer:
...     def __init__(self, val):
...         self.val = val
...     def __str__(self):                  # Używany dla instancji
...         return str(self.val)            # Przekształcenie na ciąg znaków
...
>>> objs = [Printer(2), Printer(3)]
>>> for x in objs: print(x)                 # __str__ jest wywoływane przy wyświetlaniu instancji,
...                                         # ale nie w przypadku, gdy instancja jest elementem listy!
2
3
>>> print(objs)
[<__main__.Printer object at 0x025D06F0>, <__main__.Printer object at ...more...
>>> objs
[<__main__.Printer object at 0x025D06F0>, <__main__.Printer object at ...more...


>>> class Printer:
...     def __init__(self, val):
...         self.val = val
...     def __repr__(self):                 # __repr__ wywoływane przez print, jeśli nie ma __str__
...         return str(self.val)            # __repr__ wywoływane w konsoli i przy zagnieżdżeniach
...
>>> objs = [Printer(2), Printer(3)]
>>> for x in objs: print(x)                 # Nie ma __str__: wywołuje __repr__
...
2
3
>>> print(objs)                             # Wywołuje __repr__, nie ___str__
[2, 3]
>>> objs
[2, 3]


>>> class Commuter:
...    def __init__(self, val):
...       self.val = val
...    def __add__(self, other):
...       print('add', self.val, other)
...       return other + self.val
...    def __radd__(self, other):
...       print('radd', self.val, other)
...       return other + self.val
...
>>> x = Commuter(88)
>>> y = Commuter(99)
>>> x + 1                                    # __add__: instancja + nieinstancja
add 88 1
89
>>> 1 + y                                    # __radd__: nieinstancja + instancja
radd 99 1
100
>>> x + y                                    # __add__: instancja + instancja, wywołuje __radd__
add 88 <__main__.Commuter instance at 0x0086C3D8>
radd 99 88
187


>>> class Commuter:                    # Przeniesienie klasy operandu na wynik
...     def __init__(self, val):
...         self.val = val
...     def __add__(self, other):
...         if isinstance(other, Commuter): other = other.val
...         return Commuter(self.val + other)
...     def __radd__(self, other):
...         return Commuter(other + self.val)
...     def __str__(self):
...         return '<Commuter: %s>' % self.val
...
>>> x = Commuter(88)
>>> y = Commuter(99)
>>> print(x + 10)                      # Wynik jest instancją klasy Commuter
<Commuter: 98>
>>> print(10 + y)
<Commuter: 109>

>>> z = x + y                          # Niezagnieżdżone: nie wywołuje __radd__ rekurencyjnie
>>> print(z)
<Commuter: 187>
>>> print(z + 10)
<Commuter: 197>
>>> print(z + z)
<Commuter: 374>


>>> class Number:
...     def __init__(self, val):
...         self.val = val
...     def __iadd__(self, other):             # __iadd__ wywołuje: x += y
...         self.val += other                  # Z reguły zwraca self
...         return self
...
>>> x = Number(5)
>>> x += 1
>>> x += 1
>>> x.val
7
>>> class Number:
...     def __init__(self, val):
...         self.val = val
...     def __add__(self, other):              # __add__: x = (x + y)
...         return Number(self.val + other)    # Przeniesienie typu na wynik
...
>>> x = Number(5)
>>> x += 1
>>> x += 1
>>> x.val
7


>>> class Callee:
...     def __call__(self, *pargs, **kargs):     # Przechwytuje wywołania instancji
...         print('Wywołanie:', pargs, kargs)    # Akceptuje dowolne argumenty
...
>>> C = Callee()
>>> C(1, 2, 3)                                   # C jest obiektem wywoływanym
Wywołanie: (1, 2, 3) {}
>>> C(1, 2, 3, x=4, y=5)
Wywołanie: (1, 2, 3) {'y': 5, 'x': 4}


class C:
    def __call__(self, a, b, c=5, d=6): ...     # Argumenty zwykłe i domyślne

class C:
    def __call__(self, *pargs, **kargs): ...    # Dowolne argumenty

class C:
    def __call__(self, *pargs, d=6, **kargs): ...   # Argumenty mogące być tylko słowami kluczowymi w 3.0


X = C()
X(1, 2)                                            # Pominięcie argumentów domyślnych
X(1, 2, 3, 4)                                      # Argumenty pozycyjne
X(a=1, b=2, d=4)                                   # Argumenty ze słowami kluczowymi
X(*[1, 2], **dict(c=3, d=4))                       # Rozpakowanie dowolnych argumentów
X(1, *(2,), c=3, **dict(d=4))                      # Tryb mieszany


>>> class Prod:
...     def __init__(self, value):         # Przyjmuje jeden argument
...        self.value = value
...     def __call__(self, other):
...        return self.value * other
...
>>> x = Prod(2)                             # "Zapamiętuje" wartość 2
>>> x(3)                                    # 3 (przekazane) * 2 (zapamiętane)
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 C:
    data = 'spam'
    def __gt__(self, other):               # 3.0 i 2.6
        return self.data > other
    def __lt__(self, other):
        return self.data < other

X = C()
print(X > 'ham')                           # True (wywołuje __gt__)
print(X < 'ham')                           # False (wywołuje __lt__)


class C:
    data = 'spam'                          # Tylko 2.6
    def __cmp__(self, other):              # __cmp__ nieużywane w 3.0
        return cmp(self.data, other)       # cmp niezdefiniowane w 3.0

X = C()
print(X > 'ham')                           # True (wywołuje __cmp__)
print(X < 'ham')                           # False (wywołuje __cmp__)


class C:
    data = 'spam'
    def __cmp__(self, other):
        return (self.data > other) - (self.data < other)


>>> class Truth:
...     def __bool__(self): return True
...
>>> X = Truth()
>>> if X: print('tak!')
...
tak!

>>> class Truth:
...     def __bool__(self): return False
...
>>> X = Truth()
>>> bool(X)
False


>>> class Truth:
...     def __len__(self): return 0
...
>>> X = Truth()
>>> if not X: print('nie!')
...
nie!


>>> class Truth:
...     def __bool__(self): return True          # 3.0 najpierw wywołuje __bool__
...     def __len__(self): return 0              # 2.6 najpierw wywołuje __len__
...
>>> X = Truth()
>>> if X: print('tak!')
...
tak!


>>> class Truth:
...     pass
...
>>> X = Truth()
>>> bool(X)
True


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


C:\misc> c:\python30\python
>>> class C:
...     def __bool__(self):
...         print('bool')
...         return False
...
>>> X = C()
>>> bool(X)
bool
False
>>> if X: print(99)
...
bool


C:\misc> c:\python26\python
>>> class C:
...     def __bool__(self):
...         print('bool')
...         return False
...
>>> X = C()
>>> bool(X)
True
>>> if X: print(99)
...
99


C:\misc> c:\python26\python
>>> class C:
...     def __nonzero__(self):
...         print('nie zero')
...         return False
...
>>> X = C()
>>> bool(X)
nie zero
False
>>> if X: print(99)
...
nie zero
