9. Třídy

Třídy umožňují sdružovat data a funkčnost. Vytvořením nové třídy vzniká nový typ objektu, jehož nové instance lze následně vytvářet. Ke každé instanci třídy mohou být připojeny atributy uchovávající její stav. Instance tříd mohou mít také metody (definované jejich třídou), které tento stav mění.

Ve srovnání s jinými programovacími jazyky přidává mechanismus tříd v Pythonu jen minimum nové syntaxe a sémantiky. Je směsí mechanismů tříd z jazyků C++ a Modula-3. Třídy v Pythonu poskytují všechny standardní vlastnosti objektově orientovaného programování: dědičnost umožňuje více základních tříd, odvozená třída může překrýt libovolnou metodu svých základních tříd a metoda může volat stejnojmennou metodu základní třídy. Objekty mohou obsahovat libovolné množství dat libovolného druhu. Stejně jako moduly využívají třídy dynamickou povahu Pythonu: vytvářejí se za běhu a po vytvoření je lze dále upravovat.

V terminologii C++ jsou členy třídy (včetně datových členů) běžně veřejné (s výjimkou popsanou v části Privátní proměnné) a všechny členské funkce jsou virtuální. Stejně jako v jazyce Modula-3 neexistuje zkrácený zápis pro odkazy na členy objektu z jeho metod: funkce metody se deklaruje s explicitním prvním argumentem představujícím objekt, který je při volání předán implicitně. Stejně jako ve Smalltalku jsou samotné třídy objekty. To poskytuje sémantiku pro import a přejmenování. Na rozdíl od C++ a Modula-3 lze vestavěné typy použít jako základní třídy, které může uživatel rozšířit. Podobně jako v C++ lze také pro instance tříd předefinovat většinu vestavěných operátorů se speciální syntaxí (aritmetické operátory, indexování atd.).

(Protože pro popis tříd neexistuje všeobecně přijímaná terminologie, budu občas používat pojmy ze Smalltalku a C++. Použil bych terminologii Modula-3, protože jeho objektově orientovaná sémantika je Pythonu bližší než sémantika C++, ale předpokládám, že o něm slyšelo jen málo čtenářů.)

9.1. Několik slov o názvech a objektech

Objekty mají vlastní identitu a ke stejnému objektu může být navázáno více názvů (v různých oborech platnosti). V jiných jazycích se to označuje jako aliasing (vytváření aliasů). Při prvním seznámení s Pythonem se tato vlastnost obvykle neprojeví a u neměnných základních typů (čísel, řetězců, n-tic) ji lze bezpečně ignorovat. U měnitelných objektů, jako jsou seznamy, slovníky a většina ostatních typů, však může mít překvapivý vliv na sémantiku kódu. Zpravidla je to programu ku prospěchu, protože aliasy se v některých ohledech chovají jako ukazatele. Předání objektu je například levné, protože implementace předává pouze ukazatel; pokud funkce změní objekt předaný jako argument, volající tuto změnu uvidí. Není proto zapotřebí dvou různých mechanismů předávání argumentů jako v Pascalu.

9.2. Obory platnosti a jmenné prostory v Pythonu

Než představíme třídy, musíme si nejprve říci něco o pravidlech oborů platnosti v Pythonu. Definice tříd používají několik zajímavých postupů s jmennými prostory a k jejich úplnému pochopení potřebujete vědět, jak obory platnosti a jmenné prostory fungují. Znalost tohoto tématu se ostatně hodí každému pokročilému programátorovi v Pythonu.

Začněme několika definicemi.

Jmenný prostor je mapování názvů na objekty. Většina jmenných prostorů je v současnosti implementována jako slovníky Pythonu, což obvykle není nijak patrné (s výjimkou výkonu) a v budoucnu se to může změnit. Příkladem jsou množina vestavěných názvů (obsahující funkce jako abs() a názvy vestavěných výjimek), globální názvy v modulu a lokální názvy při volání funkce. V jistém smyslu tvoří jmenný prostor také množina atributů objektu. Důležité je, že mezi názvy v různých jmenných prostorech neexistuje žádný vztah. Dva různé moduly mohou například bez nejasností definovat funkci maximize — uživatelé modulů před její název uvedou název modulu.

Slovo atribut zde mimochodem označuje každý název následující za tečkou — například ve výrazu z.real je real atributem objektu z. Přesněji řečeno jsou odkazy na názvy v modulech odkazy na atributy: ve výrazu modname.funcname je modname objekt modulu a funcname jeho atribut. V tomto případě existuje přímé mapování mezi atributy modulu a globálními názvy definovanými v modulu: sdílejí stejný jmenný prostor! [1]

Atributy mohou být pouze pro čtení nebo zapisovatelné. Ve druhém případě je možné do atributů přiřazovat. Atributy modulů jsou zapisovatelné: můžete napsat modname.the_answer = 42. Zapisovatelné atributy lze také odstranit příkazem del. Například del modname.the_answer odstraní atribut the_answer z objektu označeného názvem modname.

Jmenné prostory vznikají v různých okamžicích a mají různou životnost. Jmenný prostor obsahující vestavěné názvy se vytvoří při spuštění interpretu Pythonu a nikdy se neodstraní. Globální jmenný prostor modulu vznikne při načtení definice modulu a obvykle také přetrvá až do ukončení interpretu. Příkazy prováděné na nejvyšší úrovni interpretu, ať už načtené ze skriptu, nebo zadané interaktivně, se považují za součást modulu __main__, takže mají vlastní globální jmenný prostor. (Vestavěné názvy ve skutečnosti také sídlí v modulu, který se nazývá builtins.)

Lokální jmenný prostor funkce se vytvoří při jejím volání a odstraní se, když funkce vrátí výsledek nebo vyvolá výjimku, která v ní není obsloužena. (Přesněji by bylo vhodnější říci, že se na něj zapomene.) Každé rekurzivní volání má samozřejmě vlastní lokální jmenný prostor.

Obor platnosti je textová oblast programu v Pythonu, ve které je jmenný prostor přímo přístupný. „Přímo přístupný“ zde znamená, že se nekvalifikovaný odkaz na název pokusí tento název v jmenném prostoru vyhledat.

Přestože jsou obory platnosti určeny staticky, používají se dynamicky. V každém okamžiku provádění existují tři nebo čtyři vnořené obory, jejichž jmenné prostory jsou přímo přístupné:

  • nejvnitřnější obor, který se prohledává jako první, obsahuje lokální názvy,

  • obory všech obklopujících funkcí, prohledávané od nejbližšího, obsahují nelokální, ale zároveň neglobální názvy,

  • předposlední obor obsahuje globální názvy aktuálního modulu,

  • nejvzdálenější obor, prohledávaný jako poslední, je jmenný prostor obsahující vestavěné názvy.

Je-li název deklarován jako globální, všechny odkazy a přiřazení směřují přímo do předposledního oboru obsahujícího globální názvy modulu. K přenavázání proměnných nalezených mimo nejvnitřnější obor lze použít příkaz nonlocal. Bez této deklarace jsou takové proměnné pouze pro čtení (pokus o zápis jednoduše vytvoří novou lokální proměnnou v nejvnitřnějším oboru a stejně pojmenovanou vnější proměnnou ponechá beze změny).

Lokální obor obvykle odkazuje na lokální názvy (textově) aktuální funkce. Mimo funkce odkazuje lokální obor na stejný jmenný prostor jako globální obor, tedy na jmenný prostor modulu. Definice tříd vkládají do lokálního oboru ještě další jmenný prostor.

Je důležité si uvědomit, že obory platnosti jsou určeny textově: globálním oborem funkce definované v modulu je jmenný prostor tohoto modulu bez ohledu na to, odkud nebo pod jakým aliasem je funkce volána. Vlastní vyhledávání názvů naopak probíhá dynamicky za běhu. Definice jazyka se však vyvíjí směrem ke statickému rozlišení názvů v době „kompilace“, proto se na dynamické rozlišování nespoléhejte! (Lokální proměnné se již nyní určují staticky.)

Zvláštností Pythonu je, že pokud není účinný příkaz global nebo nonlocal, přiřazení názvů vždy směřuje do nejvnitřnějšího oboru. Přiřazení nekopíruje data — pouze váže názvy k objektům. Totéž platí pro odstranění: příkaz del x odstraní vazbu x z jmenného prostoru, na který odkazuje lokální obor. Lokální obor ve skutečnosti používají všechny operace, které zavádějí nové názvy: zejména příkazy import a definice funkcí v něm vážou název modulu či funkce.

Příkaz global označuje, že určité proměnné existují v globálním oboru a mají se v něm přenavázat. Příkaz nonlocal označuje proměnné, které existují v obklopujícím oboru a mají se přenavázat tam.

9.2.1. Příklad oborů platnosti a jmenných prostorů

Tento příklad ukazuje, jak odkazovat na různé obory platnosti a jmenné prostory a jak global a nonlocal ovlivňují vazby proměnných:

def scope_test():
    def do_local():
        spam = "local spam"

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

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

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

Výstup ukázkového kódu je:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

Všimněte si, že lokální přiřazení (které je výchozí) nezměnilo vazbu spam ve funkci scope_test. Přiřazení nonlocal změnilo vazbu spam ve scope_test a přiřazení global změnilo vazbu na úrovni modulu.

Je také vidět, že před přiřazením global žádná předchozí vazba pro spam neexistovala.

9.3. První pohled na třídy

Třídy zavádějí trochu nové syntaxe, tři nové typy objektů a novou sémantiku.

9.3.1. Syntaxe definice třídy

Nejjednodušší tvar definice třídy vypadá takto:

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

Definice tříd se stejně jako definice funkcí (příkazy def) musejí provést, než začnou mít účinek. (Definici třídy lze například umístit do větve příkazu if nebo dovnitř funkce.)

Příkazy uvnitř definice třídy budou v praxi obvykle definicemi funkcí, jsou však povoleny a někdy i užitečné také jiné příkazy — vrátíme se k tomu později. Definice funkcí uvnitř třídy mají zpravidla zvláštní tvar seznamu argumentů, který určují konvence volání metod; i to bude vysvětleno později.

Při vstupu do definice třídy se vytvoří nový jmenný prostor, který se použije jako lokální obor. Všechna přiřazení lokálním proměnným proto směřují do tohoto nového prostoru. Zejména zde definice funkcí vážou názvy nových funkcí.

Při běžném opuštění definice třídy (dosažením jejího konce) se vytvoří objekt třídy. Jde v podstatě o obal obsahu jmenného prostoru vytvořeného definicí třídy; více se o objektech tříd dozvíme v následující části. Obnoví se původní lokální obor (účinný těsně před vstupem do definice) a objekt třídy se v něm naváže na název uvedený v záhlaví definice (ClassName v příkladu).

9.3.2. Objekty tříd

Objekty tříd podporují dva druhy operací: odkazy na atributy a vytváření instancí.

Odkazy na atributy používají standardní syntaxi všech odkazů na atributy v Pythonu: obj.name. Platnými názvy atributů jsou všechny názvy, které byly v jmenném prostoru třídy při vytvoření jejího objektu. Pokud tedy definice třídy vypadala takto:

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

pak MyClass.i a MyClass.f jsou platné odkazy na atributy, které vracejí celé číslo, respektive objekt funkce. Do atributů třídy lze také přiřazovat, takže hodnotu MyClass.i lze změnit přiřazením. Platným atributem je rovněž __doc__, který vrací dokumentační řetězec třídy: "A simple example class".

Vytvoření instance třídy používá zápis funkce. Stačí si představit, že objekt třídy je funkce bez parametrů vracející novou instanci třídy. Například (pro výše uvedenou třídu):

x = MyClass()

vytvoří novou instanci třídy a přiřadí tento objekt lokální proměnné x.

Operace vytvoření instance („volání“ objektu třídy) vytvoří prázdný objekt. Mnoho tříd chce vytvářet instance přizpůsobené určitému počátečnímu stavu. Třída proto může definovat speciální metodu __init__(), například:

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

Definuje-li třída metodu __init__(), vytvoření instance automaticky zavolá __init__() pro nově vytvořenou instanci. V tomto příkladu tedy novou inicializovanou instanci získáme pomocí:

x = MyClass()

Metoda __init__() může samozřejmě pro větší flexibilitu přijímat argumenty. Argumenty předané při vytváření instance se v takovém případě předají metodě __init__(). Například:

>>> 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. Objekty instancí

Co lze s objekty instancí dělat? Jediné operace, kterým rozumějí, jsou odkazy na atributy. Existují dva druhy platných názvů atributů: datové atributy a metody.

Datové atributy odpovídají „instančním proměnným“ ve Smalltalku a „datovým členům“ v C++. Nemusejí se deklarovat; podobně jako lokální proměnné vzniknou při prvním přiřazení. Je-li například x výše vytvořenou instancí MyClass, následující kód vypíše hodnotu 16 a nezanechá po sobě žádnou stopu:

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

Druhým typem odkazu na atribut instance je metoda. Metoda je funkce, která „patří“ objektu.

Platné názvy metod objektu instance závisejí na jeho třídě. Každý atribut třídy, který je objektem funkce, z definice určuje odpovídající metodu jejích instancí. V našem příkladu je tedy x.f platným odkazem na metodu, protože MyClass.f je funkce, ale x.i nikoli, protože MyClass.i funkcí není. x.f však není totéž co MyClass.f — jde o objekt metody, nikoli objekt funkce.

9.3.4. Objekty metod

Metoda se obvykle volá bezprostředně po svém navázání:

x.f()

Je-li x = MyClass() jako výše, vrátí se řetězec 'hello world'. Metodu však není nutné volat ihned: x.f je objekt metody, který lze uložit a zavolat později. Například:

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

bude vypisovat hello world až do skonání věků.

Co přesně se stane při volání metody? Možná jste si všimli, že x.f() bylo výše zavoláno bez argumentu, přestože definice funkce f() argument uvádí. Kam se argument poděl? Python přece vyvolá výjimku, pokud je funkce vyžadující argument zavolána bez něj — i když jej ve skutečnosti nepoužívá…

Odpověď jste možná uhodli: zvláštností metod je, že se objekt instance předává jako první argument funkce. V našem příkladu je volání x.f() přesně ekvivalentní MyClass.f(x). Obecně je volání metody se seznamem n argumentů ekvivalentní volání odpovídající funkce se seznamem argumentů, před který je vložen objekt instance dané metody.

Metody obecně fungují následovně. Při odkazu na nedatový atribut instance se prohledá její třída. Označuje-li název platný atribut třídy, který je objektem funkce, odkazy na objekt instance a objekt funkce se společně zabalí do objektu metody. Při zavolání objektu metody se z objektu instance a seznamu argumentů vytvoří nový seznam argumentů a s ním se zavolá objekt funkce.

9.3.5. Třídní a instanční proměnné

Obecně platí, že instanční proměnné slouží pro data jedinečná pro jednotlivé instance, zatímco třídní proměnné slouží pro atributy a metody sdílené všemi instancemi třídy:

class Dog:

    kind = 'canine'         # class variable shared by all instances

    def __init__(self, name):
        self.name = name    # instance variable unique to each instance

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind                  # shared by all dogs
'canine'
>>> e.kind                  # shared by all dogs
'canine'
>>> d.name                  # unique to d
'Fido'
>>> e.name                  # unique to e
'Buddy'

Jak bylo uvedeno v části Několik slov o názvech a objektech, sdílená data mohou mít u měnitelných objektů, jako jsou seznamy a slovníky, překvapivé důsledky. Seznam tricks v následujícím kódu by například neměl být třídní proměnnou, protože by všechny instance Dog sdílely jediný seznam:

class Dog:

    tricks = []             # mistaken use of a class variable

    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('roll over')
>>> e.add_trick('play dead')
>>> d.tricks                # unexpectedly shared by all dogs
['roll over', 'play dead']

Správný návrh třídy má místo toho použít instanční proměnnou:

class Dog:

    def __init__(self, name):
        self.name = name
        self.tricks = []    # creates a new empty list for each dog

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

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']

9.4. Různé poznámky

Pokud se stejný název atributu vyskytuje v instanci i ve třídě, má při vyhledávání atributu přednost instance:

>>> class Warehouse:
...    purpose = 'storage'
...    region = 'west'
...
>>> w1 = Warehouse()
>>> print(w1.purpose, w1.region)
storage west
>>> w2 = Warehouse()
>>> w2.region = 'east'
>>> print(w2.purpose, w2.region)
storage east

Na datové atributy mohou odkazovat metody i běžní uživatelé („klienti“) objektu. Třídy tedy nelze použít k implementaci čistě abstraktních datových typů. Python ve skutečnosti neposkytuje nic, čím by bylo možné vynutit skrývání dat — vše je založeno na konvencích. (Implementace Pythonu napsaná v C však v případě potřeby může zcela skrýt podrobnosti implementace a řídit přístup k objektu; toho mohou využívat rozšíření Pythonu napsaná v C.)

Klienti by měli datové atributy používat opatrně — jejich přepsáním mohou porušit invarianty udržované metodami. Klienti mohou k objektu instance přidávat vlastní datové atributy, aniž ovlivní správnost metod, pokud se vyhnou konfliktům názvů. I zde může vhodná konvence pojmenování ušetřit mnoho potíží.

Pro odkazy na datové atributy (ani jiné metody) zevnitř metod neexistuje zkrácený zápis. To ve skutečnosti zvyšuje čitelnost metod: při jejich procházení nelze zaměnit lokální a instanční proměnné.

První argument metody se často nazývá self. Jde pouze o konvenci: název self nemá pro Python žádný zvláštní význam. Nedodržení této konvence však může snížit čitelnost kódu pro ostatní programátory a také je možné, že na ní bude záviset nějaký program pro procházení tříd.

Každý objekt funkce, který je atributem třídy, definuje metodu jejích instancí. Definice funkce nemusí být textově uzavřena v definici třídy: přípustné je také přiřadit objekt funkce lokální proměnné ve třídě. Například:

# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1

    def g(self):
        return 'hello world'

    h = g

f, g a h jsou nyní atributy třídy C, které odkazují na objekty funkcí, a jsou proto metodami instancí C; h je přitom zcela ekvivalentní g. Tento postup obvykle pouze mate čtenáře programu.

Metody mohou volat jiné metody prostřednictvím atributů argumentu self:

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

    def add(self, x):
        self.data.append(x)

    def addtwice(self, x):
        self.add(x)
        self.add(x)

Metody mohou odkazovat na globální názvy stejně jako běžné funkce. Globálním oborem spojeným s metodou je modul obsahující její definici. (Třída se jako globální obor nikdy nepoužívá.) Dobrý důvod k použití globálních dat v metodě se najde zřídka, globální obor má však mnoho oprávněných využití: metody mohou například používat funkce a moduly importované do globálního oboru i funkce a třídy v něm definované. V tomto globálním oboru je obvykle definována i třída obsahující metodu a v následující části uvidíme, proč může metoda chtít odkazovat na vlastní třídu.

Každá hodnota je objekt, a má proto třídu (označovanou také jako typ). Ta je uložena jako object.__class__.

9.5. Dědičnost

Jazyková vlastnost by si samozřejmě nezasloužila název „třída“, kdyby nepodporovala dědičnost. Syntaxe definice odvozené třídy vypadá takto:

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

Název BaseClassName musí být definován ve jmenném prostoru přístupném z oboru obsahujícího definici odvozené třídy. Místo názvu základní třídy jsou povoleny také jiné libovolné výrazy. To se hodí například tehdy, když je základní třída definována v jiném modulu:

class DerivedClassName(modname.BaseClassName):

Definice odvozené třídy se provádí stejně jako definice základní třídy. Při konstrukci objektu třídy se základní třída zapamatuje. Používá se při rozlišování odkazů na atributy: pokud požadovaný atribut není nalezen ve třídě, hledání pokračuje v základní třídě. Je-li základní třída sama odvozena od jiné třídy, uplatní se toto pravidlo rekurzivně.

Na vytváření instancí odvozených tříd není nic zvláštního: DerivedClassName() vytvoří novou instanci třídy. Odkazy na metody se rozlišují vyhledáním odpovídajícího atributu třídy a v případě potřeby postupem po řetězci základních tříd. Odkaz na metodu je platný, pokud se tak nalezne objekt funkce.

Odvozené třídy mohou překrývat metody svých základních tříd. Protože metody při volání jiných metod stejného objektu nemají žádná zvláštní oprávnění, může metoda základní třídy volající jinou metodu definovanou v téže třídě nakonec zavolat metodu odvozené třídy, která ji překrývá. (Pro programátory v C++: všechny metody v Pythonu jsou v podstatě virtual.)

Překrývající metoda v odvozené třídě může chtít stejnojmennou metodu základní třídy rozšířit, nikoli pouze nahradit. Základní metodu lze jednoduše zavolat přímo pomocí BaseClassName.methodname(self, arguments). To může být občas užitečné i pro klienty. (Funguje to pouze tehdy, je-li základní třída dostupná pod názvem BaseClassName v globálním oboru.)

Python nabízí dvě vestavěné funkce pracující s dědičností:

  • Funkce isinstance() ověřuje typ instance: isinstance(obj, int) bude True pouze tehdy, je-li obj.__class__ třída int nebo třída od ní odvozená.

  • Funkce issubclass() ověřuje dědičnost tříd: issubclass(bool, int) je True, protože bool je podtřídou int. Naproti tomu issubclass(float, int) je False, protože float podtřídou int není.

9.5.1. Vícenásobná dědičnost

Python podporuje také vícenásobnou dědičnost. Definice třídy s více základními třídami vypadá takto:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

Ve většině jednoduchých případů si lze hledání atributů zděděných z rodičovské třídy představit jako průchod do hloubky zleva doprava, který při překryvu v hierarchii neprohledává tutéž třídu dvakrát. Pokud tedy atribut není nalezen v DerivedClassName, hledá se v Base1, poté rekurzivně v jejích základních třídách, a není-li nalezen ani tam, v Base2 a tak dále.

Ve skutečnosti je situace o něco složitější: pořadí rozlišování metod se dynamicky mění, aby podporovalo kooperativní volání super(). V některých jiných jazycích s vícenásobnou dědičností se tento přístup nazývá call-next-method a je výkonnější než volání super v jazycích s jednoduchou dědičností.

Dynamické pořadí je nutné, protože každá vícenásobná dědičnost obsahuje jeden nebo více diamantových vztahů, v nichž je alespoň jedna rodičovská třída dosažitelná z nejnižší třídy více cestami. Všechny třídy například dědí z object, takže při vícenásobné dědičnosti k ní vždy vede více než jedna cesta. Dynamický algoritmus proto linearizuje pořadí hledání tak, aby zachoval pořadí zleva doprava určené v každé třídě, navštívil každého rodiče jen jednou a byl monotónní (odvození podtřídy nezmění pořadí přednosti jejích rodičů). Tyto vlastnosti společně umožňují navrhovat spolehlivé a rozšiřitelné třídy s vícenásobnou dědičností. Podrobnosti viz The Python 2.3 Method Resolution Order.

9.6. Privátní proměnné

„Privátní“ instanční proměnné přístupné pouze zevnitř objektu v Pythonu neexistují. Většina kódu však dodržuje konvenci, podle níž má být název s počátečním podtržítkem (např. _spam) považován za neveřejnou část API, ať už jde o funkci, metodu nebo datový člen. Jde o detail implementace, který se může bez upozornění změnit.

Protože členy privátní pro třídu mají oprávněné využití (brání konfliktům s názvy definovanými podtřídami), existuje omezená podpora mechanismu nazývaného komolení názvů <name mangling>. Každý identifikátor ve tvaru __spam (nejméně dvě úvodní a nejvýše jedno koncové podtržítko) se textově nahradí _classname__spam, kde classname je název aktuální třídy bez úvodních podtržítek. Komolení proběhne bez ohledu na syntaktickou pozici identifikátoru, pokud se nachází v definici třídy.

Viz také

Podrobnosti a zvláštní případy obsahuje specifikace komolení privátních názvů.

Komolení názvů umožňuje podtřídám překrývat metody bez narušení volání metod uvnitř třídy. Například:

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

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update   # private copy of original update() method

class MappingSubclass(Mapping):

    def update(self, keys, values):
        # provides new signature for update()
        # but does not break __init__()
        for item in zip(keys, values):
            self.items_list.append(item)

Příklad by fungoval, i kdyby MappingSubclass zavedla identifikátor __update, protože ve třídě Mapping se nahradí _Mapping__update a ve třídě MappingSubclass názvem _MappingSubclass__update.

Pravidla komolení jsou určena především k zabránění nehodám; k proměnné považované za privátní lze stále přistupovat a měnit ji. Ve zvláštních okolnostech, například v ladicím programu, to může být dokonce užitečné.

Kód předaný funkcím exec() nebo eval() nepovažuje název volající třídy za aktuální třídu. Podobá se to účinku příkazu global, který je rovněž omezen na společně zkompilovaný bajtkód. Stejné omezení platí pro getattr(), setattr() a delattr() i pro přímé odkazy na __dict__.

9.7. Další poznámky

Někdy je užitečné mít datový typ podobný „záznamu“ v Pascalu nebo „struktuře“ v C, který sdružuje několik pojmenovaných datových položek. Idiomatickým řešením je použít modul dataclasses:

from dataclasses import dataclass

@dataclass
class Employee:
    name: str
    dept: str
    salary: int
>>> john = Employee('john', 'computer lab', 1000)
>>> john.dept
'computer lab'
>>> john.salary
1000

Kódu v Pythonu, který očekává určitý abstraktní datový typ, lze často předat třídu napodobující metody tohoto typu. Máte-li například funkci formátující data ze souborového objektu, můžete definovat třídu s metodami read() a readline(), které data místo toho získávají z vyrovnávací paměti řetězce, a předat ji jako argument.

Také objekty instančních metod mají atributy: m.__self__ je objekt instance s metodou m() a m.__func__ je objekt funkce odpovídající této metodě.

9.8. Iterátory

Pravděpodobně jste si již všimli, že většinu kontejnerových objektů lze procházet pomocí příkazu for:

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("myfile.txt"):
    print(line, end='')

Tento způsob přístupu je jasný, stručný a pohodlný. Iterátory prostupují Pythonem a sjednocují jej. Příkaz for na pozadí zavolá pro kontejnerový objekt funkci iter(). Ta vrátí objekt iterátoru definující metodu __next__(), která postupně přistupuje k jednotlivým prvkům kontejneru. Jakmile žádné další prvky nezbývají, metoda __next__() vyvolá výjimku StopIteration, která cyklu for oznámí, že má skončit. Metodu __next__() lze volat vestavěnou funkcí next(); následující příklad ukazuje celý postup:

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

Když znáte mechanismus protokolu iterátoru, můžete snadno přidat chování iterátoru vlastním třídám. Definujte metodu __iter__(), která vrací objekt s metodou __next__(). Definuje-li třída __next__(), může __iter__() jednoduše vracet self:

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
m
a
p
s

9.9. Generátory

Generátory jsou jednoduchým a účinným nástrojem pro vytváření iterátorů. Zapisují se jako běžné funkce, ale při vracení dat používají příkaz yield. Při každém volání next() pokračuje generátor tam, kde skončil (pamatuje si všechny hodnoty dat i naposledy provedený příkaz). Příklad ukazuje, jak snadné může být generátor vytvořit:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
>>> for char in reverse('golf'):
...     print(char)
...
f
l
o
g

Vše, co lze provést generátory, lze provést také iterátory založenými na třídách popsanými v předchozí části. Generátory jsou tak stručné proto, že se metody __iter__() a __next__() vytvářejí automaticky.

Další klíčovou vlastností je automatické uchovávání lokálních proměnných a stavu provádění mezi voláními. Funkce se díky tomu píše snadněji a je mnohem čitelnější než řešení s instančními proměnnými jako self.index a self.data.

Kromě automatického vytváření metod a uchovávání stavu programu generátory při ukončení automaticky vyvolají StopIteration. Tyto vlastnosti společně umožňují vytvářet iterátory stejně snadno jako běžné funkce.

9.10. Generátorové výrazy

Některé jednoduché generátory lze stručně zapsat jako výrazy se syntaxí podobnou generátorové notaci seznamu, ale s kulatými namísto hranatých závorek. Tyto výrazy jsou určeny pro situace, kdy generátor okamžitě použije obklopující funkce. Generátorové výrazy jsou stručnější, ale méně všestranné než úplné definice generátorů a bývají paměťově úspornější než odpovídající generátorové notace seznamů.

Příklady:

>>> sum(i*i for i in range(10))                 # sum of squares
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
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']

Poznámky pod čarou