9. Klasy
********

Klasy umożliwiają łączenie danych i funkcjonalności. Tworzenie nowej
klasy, tworzy nowy *typ* obiektu, umożliwiając tworzenie nowych
*instancji* tego typu. Do każdej instancji klasy można przypisać
atrybuty służące do utrzymywania jej stanu. Instancje klas mogą
również posiadać metody (zdefiniowane przez klasę) umożliwiające
modyfikację ich stanu.

W porównaniu do innych języków programowania, w Pythonie, mechanizm
dodawania nowych klas wymaga niewielkiej ilości nowej składni i
semantyki. Jest to połączenie mechanizmu klas, które można znaleźć w
C++ i Modula-3. Klasy w Pythonie dostarczają wszystkie standardowe
cechy programowania obiektowego: mechanizm dziedziczenia klas pozwala
na wiele klas bazowych, klasy pochodne mogą nadpisać każdą metodę
klasy lub klas bazowych i metoda może wywołać metody klas bazowych o
tej samej nazwie. Obiekty mogą zawierać dowolną ilość i rodzaj danych.
Zarówno klasy jak i moduły są częścią dynamicznej natury Pythona: są
tworzone w trakcie działania programu i mogą być modyfikowane później,
po stworzeniu.

Korzystając z terminologii C++, składniki klas (także pola) są
*publiczne* (z wyjątkiem zobacz poniżej Zmienne prywatne), a wszystkie
metody są *wirtualne*. Podobnie jak w Moduli-3, nie ma skrótów
pozwalających na odnoszenie się do składników klas z ich metod: metoda
jest deklarowana poprzez podanie wprost jako pierwszego argumentu
obiektu, który w czasie wywołania metody zostanie jej przekazany
niejawnie. Podobnie jak w Smalltalku, same klasy także są obiektami.
Dostarcza nam to wyrażeń semantycznych pozwalających na importowanie i
zmianę nazw klasy. Inaczej niż w C++ i Moduli-3 wbudowane typy mogą
stanowić klasy, z których klasa użytkownika będzie dziedziczyć.
Podobnie jak w C++, większość wbudowanych operatorów ze specjalną
składnią (operatory arytmetyczne, indeksowanie) może być
przedefiniowane przez instancje klasy.

(Z powodu braku ogólnie zaakceptowanej terminologii w kontekście klas,
będę używał terminów ze Smalltalk i C++. Użyłbym Modula-3 ponieważ
semantyka jego programowania obiektowego jest bliższa Pythonowi niż
C++ ale zakładam, że mniej czytelników o nim słyszało.)


9.1. Kilka słów o nazwach i obiektach
=====================================

Obiekty mają indywidualność, a wiele nazw (w wielu zakresach) może być
powiązanych z tym samym obiektem. Jest to znane jako aliasing w innych
językach. Zwykle nie jest to doceniane na pierwszy rzut oka w Pythonie
i można je bezpiecznie zignorować, gdy mamy do czynienia z
niezmiennymi typami podstawowymi (liczby, ciągi znaków, krotki).
Jednak aliasing ma prawdopodobnie zaskakujący wpływ na semantykę kodu
Pythona, który obejmuje zmienne obiekty, takie jak listy, słowniki i
większość innych typów. Jest to zwykle wykorzystywane z korzyścią dla
programu, ponieważ aliasy pod pewnymi względami zachowują się jak
wskaźniki. Na przykład przekazanie obiektu jest tanie, ponieważ
implementacja przekazuje tylko wskaźnik; a jeśli funkcja modyfikuje
obiekt przekazany jako argument, wywołujący zobaczy zmianę ---
eliminuje to potrzebę stosowania dwóch różnych mechanizmów
przekazywania argumentów, jak w Pascalu.


9.2. Zasięgi widoczności i przestrzenie nazw w Pythonie
=======================================================

Przed wprowadzeniem klas, najpierw muszę powiedzieć ci coś o zasadach
zakresu Pythona. Definicje klas stosują kilka zgrabnych sztuczek z
przestrzeniami nazw, a żeby w pełni zrozumieć, co się dzieje, trzeba
wiedzieć, jak działają zakresy i przestrzenie nazw.

Zacznijmy od kilku definicji.

*Przestrzeń nazw* to odwzorowanie z nazw na obiekty. Większość
przestrzeni nazw jest obecnie implementowana jako słowniki Pythona,
ale to zwykle nie jest zauważalne w żaden sposób (z wyjątkiem
wydajności), a to może się zmienić w przyszłości. Przykładami
przestrzeni nazw są: zbiór nazw wbudowanych (zawierający funkcje np.
"abs()" i nazwy wbudowanych wyjątków); nazwy globalne w module; oraz
nazwy lokalne w wywołaniu funkcji. W pewnym sensie zbiór atrybutów
obiektu również tworzy przestrzeń nazw. Ważną rzeczą, którą należy
wiedzieć o przestrzeniach nazw, jest to, że nie ma absolutnie żadnych
relacji między nazwami w różnych przestrzeniach nazw; na przykład, dwa
różne moduły mogą zdefiniować funkcję "maximize" bez zamieszania ---
użytkownicy modułów muszą poprzedzić go nazwą modułu.

Nawiasem mówiąc, używam słowa *atrybut* dla każdej nazwy następującej
po kropce --- na przykład w wyrażeniu "z.real", "real" jest atrybutem
obiektu "z". Ściśle mówiąc, odniesienia do nazw w modułach są
odniesieniami atrybutowymi: w wyrażeniu "modname.funcname", "modname"
jest obiektem modułu, a "funcname" jest jego atrybutem. W tym
przypadku istnieje proste odwzorowanie między atrybutami modułu i
nazwami globalnymi zdefiniowanymi w module: mają tę samą przestrzeń
nazw! [1]

Atrybuty mogą być tylko do odczytu lub zapisywalne. W tym drugim
przypadku możliwe jest przypisanie do atrybutu. Atrybuty modułu są
zapisywalne: można zapisać "modname.the_answer = 42".  Zapisywalne
atrybuty można również usunąć za pomocą instrukcji "del". Na przykład,
"del modname.the_answer" usunie atrybut "the_answer" z obiektu o
nazwie "modname".

Przestrzenie nazw są tworzone w różnych momentach i mają różny czas
życia. Przestrzeń nazw zawierająca nazwy wbudowane jest tworzona
podczas uruchamiania interpretera Pythona i nigdy nie jest usuwana.
Globalna przestrzeń nazw dla modułu jest tworzona, gdy wczytywana jest
definicja modułu; zwykle przestrzenie nazw modułu również trwają do
zakończenia działania interpretera. Instrukcje wykonywane przez
wywołanie interpretera najwyższego poziomu, zarówno odczytane z pliku
skryptu, jak i interaktywnie, są uważane za część modułu o nazwie
"__main__", więc mają swoją własną globalną przestrzeń nazw. (Nazwy
wbudowane w rzeczywistości również znajdują się w module; nazwany jest
on "builtins").

Lokalna przestrzeń nazw dla funkcji jest tworzona przy wywołaniu
funkcji i usuwana, gdy funkcja zwraca wynik lub rzuca wyjątek, którego
nie obsługuje. (Właściwie, zapominanie byłoby lepszym słowem na
opisanie tego, co faktycznie się dzieje). Oczywiście, każde wywołanie
rekurencyjne ma swoją własną lokalną przestrzeń nazw.

*Zakres* to tekstowy obszar programu Python, w którym przestrzeń nazw
jest bezpośrednio dostępna. „Bezpośrednio dostępna” oznacza tutaj, że
niekwalifikowane odwołanie do nazwy próbuje znaleźć ją w przestrzeni
nazw.

Chociaż zakresy są określane statycznie, są używane dynamicznie. Cały
czas w trakcie wykonywania programu istnieją 3 lub 4 zagnieżdżone
zakresy, których przestrzenie nazw są bezpośrednio dostępne:

* najbardziej wewnętrzny zakres, który jest przeszukiwany jako
  pierwszy, zawiera nazwy lokalne

* zakresy wszystkich otaczających funkcji, które są przeszukiwane
  począwszy od najbliższego otaczającego zakresu, zawierają nazwy
  nielokalne, ale także nieglobalne

* przedostatni zakres zawiera globalne nazwy bieżącego modułu

* najbardziej zewnętrznym zakresem (przeszukiwanym jako ostatni) jest
  przestrzeń nazw zawierająca nazwy wbudowane

Jeśli nazwa jest zadeklarowana jako globalna, wtedy wszystkie
referencje i przypisania przechodzą bezpośrednio do przedostatniego
zakresu zawierającego globalne nazwy modułu.  Aby ponownie powiązać
zmienne znajdujące się poza najbardziej wewnętrznym zakresem, można
użyć instrukcji "nonlocal"; jeśli nie są zadeklarowane jako
nielokalne, zmienne te są tylko do odczytu (próba zapisu do takiej
zmiennej po prostu utworzy *nową* zmienną lokalną w najbardziej
wewnętrznym zakresie, pozostawiając identycznie nazwaną zmienną
zewnętrzną bez zmian).

Zazwyczaj zakres lokalny odwołuje się do nazw lokalnych (tekstowo)
bieżącej funkcji. Poza funkcjami, zakres lokalny odwołuje się do tej
samej przestrzeni nazw, co zakres globalny: przestrzeni nazw modułu.
Definicje klas umieszczają jeszcze jedną przestrzeń nazw w zakresie
lokalnym.

Ważne jest, aby zdać sobie sprawę, że zakresy są określane tekstowo:
globalny zakres funkcji zdefiniowany w module jest przestrzenią nazw
tego modułu, bez względu na to, skąd lub przez jaki alias funkcja jest
wywoływana. Z drugiej strony, rzeczywiste wyszukiwanie nazw odbywa się
dynamicznie, w czasie wykonywania --- jednak definicja języka ewoluuje
w kierunku statycznego rozpoznawania nazw, w czasie „kompilacji”, więc
nie należy polegać na dynamicznym rozpoznawaniu nazw!  (W
rzeczywistości zmienne lokalne są już określane statycznie).

Szczególnym dziwactwem Pythona jest to, że – jeśli nie działa
instrukcja "global" lub "nonlocal" – przypisanie do nazw zawsze trafia
do najbardziej wewnętrznego zakresu. Przypisania nie kopiują danych –
po prostu wiążą nazwy z obiektami. To samo dotyczy usuwania:
instrukcja "del x" usuwa wiązanie "x" z przestrzeni nazw, do której
odwołuje się zakres lokalny. W rzeczywistości wszystkie operacje,
które wprowadzają nowe nazwy, używają zakresu lokalnego: w
szczególności instrukcje "import" i definicje funkcji wiążą nazwę
modułu lub funkcji w zakresie lokalnym.

Instrukcja "global" może być użyta do wskazania, że określone zmienne
znajdują się w zakresie globalnym i powinny być tam ponownie wiązane;
instrukcja "nonlocal" wskazuje, że określone zmienne znajdują się w
zakresie otaczającym i powinny być tam ponownie wiązane.


9.2.1. Przykład zakresów i przestrzeni nazw
-------------------------------------------

Oto przykład pokazujący, jak odwoływać się do różnych zakresów i
przestrzeni nazw oraz jak "global" i "nonlocal" wpływają na wiązanie
zmiennych:

   def scope_test():
       def do_local():
           spam = "lokalna konserwa"

       def do_nonlocal():
           nonlocal spam
           spam = "nielokalna konserwa"

       def do_global():
           global spam
           spam = "globalna konserwa"

       spam = "testowa konserwa"
       do_local()
       print("Po przypisaniu lokalnym:", spam)
       do_nonlocal()
       print("Po przypisaniu nielokalnym:", spam)
       do_global()
       print("Po przypisaniu globalnym:", spam)

   scope_test()
   print("W globalnym zakresie:", spam)

Wyjście przykładowego kodu to:

   Po przypisaniu lokalnym: testowa konserwa
   Po przypisaniu nielokalnym: nielokalna konserwa
   Po przypisaniu globalnym: nielokalna konserwa
   W globalnym zakresie: globalna konserwa

Zauważ, że *lokalne* przypisanie (które jest domyślne) nie zmieniło
wiązania *spam* w *scope_test*. Przypisanie "nonlocal"  zmieniło
wiązanie *spam* w *scope_test*, a przypisanie "global" zmieniło
wiązanie na poziomie modułu.

Można również zauważyć, że nie było wcześniejszego powiązania dla
*spam* przed przypisaniem "global".


9.3. Pierwsze spojrzenie na klasy
=================================

Klasy wprowadzają trochę nowej składni, trzy nowe typy obiektów i
trochę nowej semantyki.


9.3.1. Składnia definicji klasy
-------------------------------

Najprostsza forma definicji klasy wygląda następująco:

   class ClassName:
       <instrukcja-1>
       .
       .
       .
       <instrukcja-N>

Definicje klas, podobnie jak definicje funkcji (instrukcje "def"),
muszą zostać wykonane, zanim będą miały jakikolwiek efekt. (Można
sobie wyobrazić umieszczenie definicji klasy w gałęzi instrukcji "if"
lub wewnątrz funkcji.)

W praktyce, instrukcje wewnątrz definicji klasy będą zwykle
definicjami funkcji, ale inne instrukcje są dozwolone, a czasem
przydatne --- wrócimy do tego później. Definicje funkcji wewnątrz
klasy zwykle mają specyficzną formę listy argumentów, podyktowaną
konwencjami wywoływania metod --- ponownie, zostanie to wyjaśnione
później.

Po wejściu w definicję klasy tworzona jest nowa przestrzeń nazw i
używana jako zakres lokalny --- a zatem wszystkie przypisania do
zmiennych lokalnych trafiają do tej nowej przestrzeni nazw. W
szczególności, definicje funkcji wiążą nazwę nowej funkcji w tej
przestrzeni nazw.

Kiedy definicja klasy jest opuszczana normalnie (przez koniec),
tworzony jest *obiekt klasy*. Jest to w zasadzie opakowanie wokół
zawartości przestrzeni nazw utworzonej przez definicję klasy; dowiemy
się więcej o obiektach klas w następnej sekcji. Oryginalny zakres
lokalny (ten, który obowiązywał tuż przed wprowadzeniem definicji
klasy) zostaje przywrócony, a obiekt klasy jest powiązany z nazwą
klasy podaną w nagłówku definicja klasy ("ClassName" w przykładzie).


9.3.2. Obiekty klas
-------------------

Obiekty klas obsługują dwa rodzaje operacji: odniesienia do atrybutów
i tworzenie instancji.

*Odniesienia do atrybutów* używają standardowej składni używanej dla
wszystkich odniesień do atrybutów w Pythonie: "obj.name".  Prawidłowe
nazwy atrybutów to wszystkie nazwy, które znajdowały się w przestrzeni
nazw klasy, gdy obiekt klasy został utworzony. Tak więc, jeśli
definicja klasy wyglądała tak:

   class MyClass:
       """Prosta przykładowa klasa"""
       i = 12345

       def f(self):
           return 'witaj świecie'

wtedy "MyClass.i" i "MyClass.f" są poprawnymi odniesieniami do
atrybutów, zwracającymi odpowiednio liczbę całkowitą i funkcję. Do
atrybutów klasowych można również przypisywać wartości, więc można
zmienić wartość "MyClass.i" przez przypisanie. "__doc__" jest również
poprawnym atrybutem, zwracającym docstring należący do klasy: ""Prosta
przykładowa klasa"".

*Instancjonowanie* klasy używa notacji funkcji. Wystarczy wyobrazić
sobie, że obiekt klasy jest bezparametrową funkcją, która zwraca nową
instancję klasy. Na przykład (zakładając powyższą klasę):

   x = MyClass()

tworzy nową *instancję* klasy i przypisuje ten obiekt do zmiennej
lokalnej "x".

Operacja instancjonowania („wywołanie” obiektu klasy) tworzy pusty
obiekt. Wiele klas lubi tworzyć obiekty z instancjami dostosowanymi do
określonego stanu początkowego. Dlatego klasa może zdefiniować
specjalną metodę o nazwie "__init__()", taką jak ta:

   def __init__(self):
       self.data = []

Gdy klasa definiuje metodę "__init__()", instancjonowanie klasy
automatycznie wywołuje "__init__()" dla nowo utworzonej instancji
klasy. Tak więc w tym przykładzie nową, zainicjalizowaną instancję
można uzyskać przez:

   x = MyClass()

Oczywiście metoda "__init__()" może mieć argumenty dla większej
elastyczności. W takim przypadku argumenty podane operatorowi
instancjonowania klasy są przekazywane do "__init__()".  Na przykład

   >>> class Complex:
   ...     def __init__(self, realpart, imagpart):
   ...         self.r = realpart
   ...         self.i = imagpart
   ...
   >>> x = Complex(3.0, -4.5)
   >>> x.r, x.i
   (3.0, -4.5)


9.3.3. Obiekty instancji
------------------------

Co możemy zrobić z obiektami instancji? Jedynymi operacjami
rozumianymi przez obiekty instancji są odniesienia do atrybutów.
Istnieją dwa rodzaje poprawnych nazw atrybutów: atrybuty danych i
metody.

*Atrybuty danych* odpowiadają „zmiennym instancji” w Smalltalku i
„składowym” w C++.  Atrybuty danych nie muszą być deklarowane;
podobnie jak zmienna lokalna, powstają, gdy zostaną po raz pierwszy
przypisane. Na przykład, jeśli "x" jest instancją "MyClass" utworzonej
powyżej, następujący fragment kodu wydrukuje wartość "16", bez
pozostawiania śladu:

   x.counter = 1
   while x.counter < 10:
       x.counter = x.counter * 2
   print(x.counter)
   del x.counter

Innym rodzajem odniesienia do atrybutu instancji jest *metoda*. Metoda
to funkcja, która „należy” do obiektu.

Prawidłowe nazwy metod obiektu instancji zależą od jego klasy. Z
definicji wszystkie atrybuty klasy, które są obiektami funkcji
definiują odpowiednie metody jej instancji. Tak więc w naszym
przykładzie, "x.f" jest poprawnym odwołaniem do metody, ponieważ
"MyClass.f" jest funkcją, ale "x.i" nie jest, ponieważ "MyClass.i" nie
jest.  Ale "x.f" nie jest tym samym co "MyClass.f" --- jest *obiektem
metody*, a nie obiektem funkcji.


9.3.4. Obiekty metod
--------------------

Zazwyczaj metoda jest wywoływana zaraz po jej powiązaniu:

   x.f()

Jeśli "x = MyClass()", jak powyżej, powyższe zwróci napis "'hello
world'" . Nie jest jednak konieczne wywoływanie metody od razu: "x.f"
jest obiektem metody i może być przechowywany i wywoływany później.
Na przykład:

   xf = x.f
   while True:
       print(xf())

będzie drukować  "witaj świecie" do końca czasu.

Co dokładnie dzieje się, gdy wywoływana jest metoda? Być może
zauważyłeś(-łaś), że powyżej "x.f()" została wywołana bez argumentu,
mimo że definicja funkcji dla "f()" określała argument. Co się stało z
argumentem? Z pewnością Python rzuca wyjątek, gdy funkcja, który
wymaga argumentu jest wywoływana bez żadnego --- nawet jeśli argument
nie jest faktycznie używany…

Właściwie, być może zgadłeś(-łaś) odpowiedź: szczególną rzeczą w
metodach jest to, że obiekt instancji jest przekazywany jako pierwszy
argument funkcji. W naszym przykładzie wywołanie "x.f()" jest
dokładnie równoważne wywołaniu "MyClass.f(x)". Ogólnie rzecz biorąc,
wywołanie metody z listą *n* argumentów jest równoważne wywołaniu
odpowiedniej funkcji z listą argumentów, która jest tworzona przez
wstawienie obiektu instancji metody przed pierwszym argumentem.

Ogólnie rzecz biorąc, metody działają w następujący sposób.  Gdy
odwoływane jest nie-dane atrybut instancji, wyszukiwana jest klasa
instancji. Jeśli nazwa wskazuje na prawidłową klasę atrybut, która
jest obiektem funkcja, odwołania zarówno do obiektu instancji, jak i
obiektu funkcja są pakowane do obiektu metody.  Gdy obiekt metody jest
wywoływany z listą argument, nowa lista argument jest konstruowana z
instancji obiektu i listy argument, a obiekt funkcja jest wywoływany z
tą nową listą argument.


9.3.5. Zmienne klas i instancji
-------------------------------

Ogólnie rzecz biorąc, zmienne instancji są dla danych unikalnych dla
każdej instancji, a zmienne klasy są dla atrybutów i metod
współdzielonych przez wszystkie instancje klasy:

   class Dog:

     kind = 'pies' # zmienna klasowa współdzielona przez wszystkie instancje

     def __init__(self, name):
       self.name = name # zmienna instancji unikalna dla każdej instancji

   >>> d = Dog('Fido')
   >>> e = Dog('Zito')
   >>> d.kind # współdzielona przez wszystkie psy
   'pies'
   >>> e.kind # współdzielona przez wszystkie psy
   'pies'
   >>> d.name # unikalna dla d
   'Fido'
   >>> e.name # unikalna dla e
   'Zito'

Jak omówiono w Kilka słów o nazwach i obiektach, współdzielone dane
mogą mieć zaskakujące efekty z udziałem *mutable* obiektów takich jak
listy i słownik. Na przykład, lista *sztuczki* w poniższym kodzie nie
powinna być używana jako zmienna klasowa, ponieważ tylko jedna lista
byłaby współdzielona przez wszystkie instancje klasy *Dog*:

   class Dog:

       tricks = []             # błędne użycie zmiennej klasowej

       def __init__(self, name):
           self.name = name

       def add_trick(self, trick):
           self.tricks.append(trick)

   >>> d = Dog('Fido')
   >>> e = Dog('Buddy')
   >>> d.add_trick('przewróć się')
   >>> e.add_trick('udawaj martwego')
   >>> d.tricks                # nieoczekiwanie współdzielone przez wszystkie psy
   ['przewróć się', 'udawaj martwego']

Prawidłowy projekt klasy powinien zamiast tego używać zmiennej
instancji:

   class Dog:

       def __init__(self, name):
           self.name = name
           self.tricks = []    # tworzy nową pustą listę dla każdego psa

       def add_trick(self, trick):
           self.tricks.append(trick)

   >>> d = Dog('Fido')
   >>> e = Dog('Zito')
   >>> d.add_trick('przewróć się')
   >>> e.add_trick('udawaj martwego')
   >>> d.tricks
   ['przewróć się']
   >>> e.tricks
   ['udawaj martwego']


9.4. Uwagi losowe
=================

Jeśli ten sam atrybut występuje zarówno w instancji, jak i w klasie,
wówczas wyszukiwanie atrybut nadaje priorytet instancji:

   >>> class Magazyn:
   ...     cel = 'przechowywanie'
   ...     region = 'zachód'
   ...
   >>> m1 = Magazyn()
   >>> print(m1.cel, m1.region)
   przechowywanie zachód
   >>> m2 = Magazyn()
   >>> m2.region = 'wschód'
   >>> print(m2.cel, m2.region)
   przechowywanie wschód

Do danych atrybut mogą odwoływać się zarówno metody, jak i zwykli
użytkownicy ("klienci") obiektu.  Innymi słowy, klasy nie nadają się
do implementacji czysto abstrakcyjnych typów danych.  W rzeczywistości
nic w Python nie umożliwia wymuszenia ukrywania danych --- wszystko
opiera się na konwencji.  (Z drugiej strony, implementacja Python,
napisana w C, może całkowicie ukryć szczegóły implementacji i
kontrolować dostęp do obiektu, jeśli to konieczne; może to być
wykorzystane przez rozszerzenia Python napisane w C).

Klienci powinni używać danych atrybut z ostrożnością --- klienci mogą
zepsuć niezmienniki utrzymywane przez metody poprzez stemplowanie ich
danych atrybut.  Należy pamiętać, że klienci mogą dodawać własne dane
atrybut do obiektu instancji bez wpływu na ważność metod, o ile unika
się konfliktów nazw --- ponownie, konwencja nazewnictwa może tutaj
zaoszczędzić wiele problemow.

Nie ma skrótu do odwoływania się do danych atrybut (lub innych metod!)
z poziomu metod.  Uważam, że w rzeczywistości zwiększa to czytelność
metod: nie ma szans na pomylenie zmiennych lokalnych i zmiennych
instancji podczas przeglądania metody.

Często pierwsza argument metody jest nazywana "self".  Jest to nic
więcej niż konwencja: nazwa "self" nie ma absolutnie żadnego
specjalnego znaczenia dla Python. Należy jednak pamiętać, że
nieprzestrzeganie tej konwencji może sprawić, że kod będzie mniej
czytelny dla innych programistów Python, a także możliwe jest
napisanie programu *przeglądarki klas*, który opiera się na takiej
konwencji.

Każdy obiekt funkcja będący klasą atrybut definiuje metodę dla
instancji tej klasy.  Nie jest konieczne, aby definicja funkcja była
tekstowo zawarta w definicja klasy: przypisanie obiektu funkcja do
zmiennej lokalnej w klasie jest również w porządku.  Na przykład:

   # funkcja zdefiniowane poza klasą
   def f1(self, x, y):
       return min(x, x+y)

   class C:
       f = f1

       def g(self):
           return 'hello world'

       h = g

Teraz "f", "g" i "h" są wszystkie atrybut klasy "C", które odnoszą się
do obiektów funkcja, a w konsekwencji wszystkie są metodami instancji
"C" --- "h" jest dokładnie równoważne "g". Zauważ, że ta praktyka
zwykle służy jedynie do zmylenia czytelnika programu.

Metody mogą wywoływać inne metody przy użyciu metody atrybut z "self"
argument

   class Torba:
       def __init__(self):
           self.dane = []

       def dodac(self, x):
           self.dane.append(x)

       def  dodacDwaRazy(self, x):
           self.dodac(x)
           self.dodac(x)

Metody mogą odwoływać się do nazw globalnych w taki sam sposób jak
zwykłe funkcje.  Zakres globalny powiązany z metodą to moduł
zawierający jej definicję.  (Klasa nigdy nie jest używana jako zakres
globalny.) Podczas gdy rzadko można napotkać dobry powód do używania
danych globalnych w metodzie, istnieje wiele uzasadnionych zastosowań
zakresu globalnego: po pierwsze, funkcja i moduł importowane do
zakresu globalnego mogą być używane przez metody, a także funkcja i
klasy w nim zdefiniowane.  Zazwyczaj klasa zawierająca metodę sama
jest zdefiniowana w tym globalnym zakresie, a w następnej sekcji
znajdziemy kilka dobrych powodów, dla których metoda chciałaby
odwoływać się do własnej klasy.

Każda wartość jest obiektem, a zatem ma *klasę* (zwaną również
*typem*). Jest ona przechowywana jako "object.__class__".


9.5. Dziedziczenie
==================

Oczywiście funkcja językowa nie byłaby godna nazwy "klasa" bez obsługi
dziedziczenia.  Składnia pochodnej definicja klasy wygląda tak:

   class DerivedClassName(BaseClassName):
       <statement-1>
       .
       .
       .
       <statement-N>

Nazwa "BaseClassName" musi być zdefiniowana w przestrzeni nazw
dostępnej z zakresu zawierającego pochodną definicja klasy.  Zamiast
nazwy klasa bazowa dozwolone są również inne dowolne wyrażenia.  Może
to być przydatne, na przykład, gdy klasa bazowa jest zdefiniowany w
innym moduł:

   class DerivedClassName(modname.BaseClassName):

Wykonanie pochodnej definicja klasy przebiega tak samo, jak w
przypadku klasa bazowa. Gdy konstruowany jest obiekt klasy,
zapamiętywany jest klasa bazowa.  Jest to wykorzystywane do
rozwiązywania referencji atrybut: jeśli żądany atrybut nie zostanie
znaleziony w klasie, wyszukiwanie jest kontynuowane w klasie bazowej.
Ta reguła jest stosowana rekurencyjnie, jeśli sama klasa bazowa jest
pochodną innej klasy.

Nie ma nic specjalnego w instancjonowaniu klas pochodnych:
"DerivedClassName()" tworzy nową instancję klasy.  Odniesienia do
metod są rozwiązywane w następujący sposób: odpowiednia klasa atrybut
jest przeszukiwana, w razie potrzeby schodząc w dół łańcucha klas
bazowych, a odniesienie do metody jest ważne, jeśli daje to obiekt
funkcja.

Klasy pochodne mogą nadpisywać metody swoich klas bazowych.  Ponieważ
metody nie mają specjalnych przywilejów podczas wywoływania innych
metod tego samego obiektu, metoda klasa bazowa, która wywołuje inną
metodę zdefiniowaną w tym samym klasa bazowa może skończyć się
wywołaniem metody klasy pochodnej, która ją nadpisuje.  (Dla
programistów C++: wszystkie metody w Python są efektywnie "virtual").

Metoda nadrzędna w klasie pochodnej może w rzeczywistości chcieć
rozszerzyć, a nie tylko zastąpić metodę klasa bazowa o tej samej
nazwie. Istnieje prosty sposób na bezpośrednie wywołanie metody klasa
bazowa: wystarczy wywołać "BaseClassName.methodname(self, arguments)".
Jest to czasami przydatne również dla klientów.  (Należy pamiętać, że
działa to tylko wtedy, gdy klasa bazowa jest dostępna jako
"BaseClassName" w zakresie globalnym).

Python ma dwa wbudowane funkcje, które pracuja z dziedziczeniem:

* Użyj "isinstance()" do sprawdzenia typu instancji: "isinstance(obj,
  int)" będzie "True" tylko wtedy, gdy "obj.__class__" jest "int" lub
  jakąś klasą pochodną od "int".

* Użyj "issubclass()" do sprawdzenia dziedziczenia klas:
  "issubclass(bool, int)" jest "True", ponieważ "bool" jest podklasa z
  "int". Jednak "issubclass(float, int)" jest "False", ponieważ
  "float" nie jest podklasa z "int".


9.5.1. Dziedziczenie wielokrotne
--------------------------------

Python obsługuje również dziedziczenie wielokrotne.  Strona definicja
klasy z wieloma klasami bazowymi wygląda tak:

   class NazwaKlasyPochodnej(Baza1, Baza2, Baza3):
       <statement-1>
       .
       .
       .
       <statement-N>

Dla większości celów, w najprostszych przypadkach, można myśleć o
wyszukiwaniu atrybut odziedziczonym po klasie nadrzędnej jako o
wyszukiwaniu w głąb, od lewej do prawej, nie szukając dwa razy w tej
samej klasie, gdy hierarchia się pokrywa. Tak więc, jeśli atrybut nie
zostanie znaleziony w "DerivedClassName", jest szukany w "Base1", a
następnie (rekurencyjnie) w klasach bazowych "Base1", a jeśli nie
zostanie tam znaleziony, jest szukany w "Base2" i tak dalej.

W rzeczywistości jest to nieco bardziej skomplikowane; kolejność
rozwiązywania metod zmienia się dynamicznie, aby wspierać kooperacyjne
wywołania "super()". Podejście to jest znane w niektórych innych
językach wielokrotnego dziedziczenia jako call-next-method i jest
bardziej wydajne niż super wywołanie występujące w językach
pojedynczego dziedziczenia.

Dynamiczne porządkowanie jest konieczne, ponieważ wszystkie przypadki
wielokrotnego dziedziczenia wykazują jedną lub więcej relacji
diamentowych (w których co najmniej jedna z klas nadrzędnych może być
dostępna poprzez wiele ścieżek z najniższej klasy).  Na przykład,
wszystkie klasy dziedziczą z "object", więc każdy przypadek
wielokrotnego dziedziczenia zapewnia więcej niż jedną ścieżkę dostępu
do "object".  Aby zapobiec wielokrotnemu dostępowi do klas bazowych,
algorytm dynamiczny linearyzuje kolejność wyszukiwania w sposób, który
zachowuje kolejność od lewej do prawej określoną w każdej klasie,
która wywołuje każdego rodzica tylko raz i która jest monotoniczna (co
oznacza, że klasa może być podklasowana bez wpływu na kolejność
pierwszeństwa jej rodziców). Łącznie właściwości te umożliwiają
projektowanie niezawodnych i rozszerzalnych klas z wielokrotnym
dziedziczeniem.  Więcej szczegółów można znaleźć na stronie The Python
2.3 Method Resolution Order.


9.6. Zmienne prywatne
=====================

"Prywatne" zmienne instancje, do których nie można uzyskać dostępu
inaczej niż z wnętrza obiektu, nie istnieją w Python. Istnieje jednak
konwencja, której przestrzega większość kodu Python: nazwa poprzedzona
podkreśleniem (np. "_spam") powinna być traktowana jako niepubliczna
część API (niezależnie od tego, czy jest to funkcja, metoda czy
członek danych).  Należy ją traktować jako szczegół implementacji i
może ona ulec zmianie bez powiadomienia.

Ponieważ istnieje uzasadniony przypadek użycia dla członków klasy-
prywatnej (mianowicie, aby uniknąć kolizji nazw z nazwami
zdefiniowanymi przez podklasa), istnieje ograniczone wsparcie dla
takiego mechanizmu, zwanego *name mangling*.  Każdy identyfikator w
postaci "__spam" (co najmniej dwa początkowe podkreślenia, co najwyżej
jedno końcowe podkreślenie) jest tekstowo zastępowany przez
"_classname__spam", gdzie "classname" jest bieżącą nazwą klasy z
usuniętymi początkowymi podkreśleniami.  Zamiana ta jest wykonywana
bez względu na pozycję składniową identyfikatora, o ile występuje on w
definicji klasy.

Zobacz także:

  Szczegółowe informacje i przypadki specjalne znajdują się w
  specyfikacji private name mangling.

Manipulowanie nazwami jest pomocne w umożliwieniu podklasa
nadpisywania metod bez przerywania wywołań metod wewnątrzklasowych.
Na przykład:

   class Mapping:
       def __init__(self, iterable):
           self.przedmiot_lista = []
           self.__update(iterable)

       def update(self, iterable):
           for przedmiot in iterable:
               self.przedmiot_lista.append(przedmiot)

       __update = update # prywatna kopia oryginalnej metody update()

   class MappingSubklasa(Mapping):

       def update(self, keys, values):
           # zapewnia nową sygnaturę dla update()
           # ale nie przerywa __init__()
           for item in zip(keys, values):
               self.items_list.append(item)

Powyższy przykład zadziała nawet wtedy, gdy "MappingSubclass"
wprowadzi identyfikator "__update", ponieważ zostanie on zastąpiony
odpowiednio "_Mapping__update" w klasie "Mapping" i
"_MappingSubclass__update" w klasie "MappingSubclass".

Należy pamiętać, że reguły mieszania zostały zaprojektowane głównie w
celu uniknięcia wypadków; nadal możliwy jest dostęp lub modyfikacja
zmiennej, która jest uważana za prywatną.  Może to być nawet przydatne
w szczególnych okolicznościach, takich jak debugger.

Zauważ, że kod przekazany do "exec()" lub "eval()" nie uważa nazwy
klasy wywołującej za bieżącą klasę; jest to podobne do efektu "global"
instrukcja , którego efekt jest również ograniczony do kodu, który
jest kompilowany bajtowo.  To samo ograniczenie dotyczy "getattr()",
"setattr()" i "delattr()", a także bezpośredniego odwoływania się do
"__dict__".


9.7. Przypadki losowe
=====================

Czasami przydatne jest posiadanie typu danych podobnego do "rekordu"
Pascala lub "struktury" C, łączącego kilka nazwanych elementów danych.
Idiomatycznym podejściem jest użycie w tym celu "dataclasses":

   from dataclasses import dataclass

   @dataclass
   class Pracownik:
       imie: str
       dzial: str
       pensja: int

   >>> stanislaw = Pracownik('stanislaw', 'pracownia komputerowa, 1000)
   >>> stanislaw.dzial
   'pracownia komputerowa'
   >>> stanislaw.pensja
   1000

Fragment kodu Python, który oczekuje określonego abstrakcyjnego typu
danych, może często zostać przekazany klasie, która emuluje metody
tego typu danych.  Na przykład, jeśli masz klasę funkcja, która
formatuje pewne dane z obiektu pliku, możesz zdefiniować klasę z
metodami "read()" i "readline()", które pobierają dane z bufora napis
i przekazują je jako argument.

Obiekty metod instancji także mają atrybuty: "m.__self__" jest
obiektem instancji z metodą "m()", i "m.__func__" jest obiektem
funkcji odpowiadającym metodzie.


9.8. Iteratory
==============

Prawdopodobnie zauważyłeś już, że większość obiektów kontenera  można
iterować za pomocą "for" instrukcja

   for element in [1, 2, 3]:
       print(element)
   for element in (1, 2, 3):
       print(element)
   for key in {'one':1, 'two':2}:
       print(key)
   for char in "123":
       print(char)
   for line in open("mojplik.txt"):
       print(line, end='')

Ten styl dostępu jest jasny, zwięzły i wygodny.  Użycie iterators
przenika i ujednolica Python.  Za kulisami, "for" instrukcja wywołuje
"iter()" na obiekcie kontenera.  Obiekt funkcja zwracać i iterator
definiują metodę "__next__()", która uzyskuje dostęp do elementów w
kontenerze jeden po drugim.  Gdy nie ma więcej elementów, "__next__()"
rzucić wyjątek "StopIteration", który informuje pętlę "for" o
zakończeniu.  Metodę "__next__()" można wywołać za pomocą "next()"
wbudowanej funkcja; ten przykład pokazuje, jak to wszystko działa:

   >>> s = 'abc'
   >>> it = iter(s)
   >>> it
   <str_iterator object at 0x10c90e650>
   >>> next(it)
   'a'
   >>> next(it)
   'b'
   >>> next(it)
   'c'
   >>> next(it)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
       next(it)
   StopIteration

Po zapoznaniu się z mechaniką stojącą za protokołem iterator, łatwo
jest dodać iterator zachowanie do swoich klas.  Zdefiniuj metodę
"__iter__()", która zwracać obiekt z metodą "__next__()".  Jeśli klasa
definiuje "__next__()", to "__iter__()" może po prostu zwracać "self"

   class Reverse:
       """iterator do zapętlania sekwencji wstecz. """
       def __init__(self, dane):
           self.dane = dane
           self.indeks = len(dane)

       def __iter__(self):
           return self

       def __next__(self):
           if self.indeks == 0:
               raise StopIteration
           self.indeks = self.indeks - 1
           return self.data[self.indeks]

   >>> rev = Reverse('spam')
   >>> iter(rev)
   <__main__.Reverse object at 0x00A1DB50>
   >>> for char in rev:
   ... print(char)
   ...
   m
   a
   p
   s


9.9. Generatory
===============

*Generatory* są prostym i potężnym narzędziem do tworzenia iteratorów.
Są one pisane jak zwykłe funkcje, ale używają instrukcji "yield" za
każdym razem, gdy chcą zwracać dane.  Za każdym razem, gdy "next()"
jest na nim wywoływany, generator wznawia działanie od miejsca, w
którym zostało przerwane (pamięta wszystkie wartości danych i które
instrukcje było ostatnio wykonywane). Przykład pokazuje, że tworzenie
generatorów może być banalnie proste:

   def reverse(dane):
       for indeks in range(len(dane)-1, -1, -1):
           yield dane[indeks]

   >>> for char in reverse('golf'):
   ...     print(char)
   ...
   f
   l
   o
   g

Wszystko, co można zrobić za pomocą generatorów, można również zrobić
za pomocą iteratorów opartych na klasach, jak opisano w poprzedniej
sekcji.  Kompaktowość generatorów polega na tym, że metody
"__iter__()" i "__next__()" są tworzone automatycznie.

Inną kluczową cechą jest to, że zmienne lokalne i stan wykonania są
automatycznie zapisywane między wywołaniami.  Sprawia to, że funkcja
jest łatwiejszy do napisania i znacznie bardziej przejrzysty niż
podejście wykorzystujące zmienne instancji, takie jak "self.index" i
"self.data".

Oprócz automatycznego tworzenia metod i zapisywania stanu programu, po
zakończeniu generatory automatycznie rzucić "StopIteration" . W
połączeniu, funkcje te ułatwiają tworzenie iteratorów bez większego
wysiłku niż napisanie zwykłego funkcja.


9.10. generator wyrażenia
=========================

Niektóre proste generatory mogą być kodowane w zwięzły sposób jako
wyrażenia przy użyciu składni podobnej do list comprehensions, ale z
nawiasami zamiast nawiasów kwadratowych. Wyrażenia te są przeznaczone
do sytuacji, w których generator jest używane od razu przez otaczającą
je funkcja. Wyrażenia generator są bardziej zwięzłe, ale mniej
wszechstronne niż pełne definicje generator i zwykle są bardziej
przyjazne dla pamięci niż równoważne wyrażenia listowe.

Przykłady:

   >>> sum(i*i for i in range(10))                 # suma kwadratów
   285

   >>> xvec = [10, 20, 30]
   >>> yvec = [7, 5, 3]
   >>> sum(x*y for x,y in zip(xvec, yvec))         # iloczyn skalarny
   260

   >>> unique_words = set(word for line in page for word in line.split())

   >>> valedictorian = max((student.gpa, student.name) for student in graduates)

   >>> data = 'golf'
   >>> list(data[i] for i in range(len(data)-1, -1, -1))
   ['f', 'l', 'o', 'g']

-[ Przypisy ]-

[1] Z wyjątkiem jednej rzeczy. Obiekty moduł mają tajną tylko do
    odczytu atrybut o nazwie "__dict__", która zwracać słownik służy
    do implementacji przestrzeni nazw moduł; nazwa "__dict__" jest
    atrybut, ale nie jest nazwą globalną. Oczywiście korzystanie z
    tego narusza abstrakcję implementacji przestrzeni nazw i powinno
    być ograniczone do takich rzeczy, jak debugery post-mortem.
