Python je objektově orientovaným jazykem již od doby své existence. Díky tomu je vytváření a používání tříd a objektů vyloženě snadné. Tato kapitola vám pomůže stát se expertem na používání podpory objektově orientovaného programování v jazyce Python.
Pokud nemáte žádné předchozí zkušenosti s objektově orientovaným programováním (OO), možná budete chtít konzultovat úvodní kurz o něm nebo alespoň nějaký výukový program, abyste měli přehled o základních pojmech.
Tady je však malý úvod do objektově orientovaného programování (OOP), který vás uvede do tempa –
Přehled terminologie OOP
-
Třída – Uživatelem definovaný prototyp objektu, který definuje sadu atributů charakterizujících libovolný objekt dané třídy. Atributy jsou datové členy (proměnné třídy a proměnné instance) a metody, ke kterým se přistupuje pomocí tečkové notace.
-
Proměnná třídy – Proměnná, která je společná pro všechny instance třídy. Proměnné třídy jsou definovány v rámci třídy, ale mimo jakoukoli její metodu. Proměnné třídy se nepoužívají tak často jako proměnné instance.
-
Datový člen – Proměnná třídy nebo proměnná instance, která obsahuje data spojená s třídou a jejími objekty.
-
Přetěžování funkce – Přiřazení více než jednoho chování určité funkci. Prováděná operace se liší podle typů zapojených objektů nebo argumentů.
-
Proměnná instance – Proměnná, která je definována uvnitř metody a patří pouze aktuální instanci třídy.
-
Dědičnost – Přenos vlastností třídy na jiné třídy, které jsou od ní odvozeny.
-
Instance – Individuální objekt určité třídy. Například objekt obj, který patří třídě Circle, je instancí třídy Circle.
-
Instalace – Vytvoření instance třídy.
-
Metoda – Zvláštní druh funkce, která je definována v definici třídy.
-
Objekt – Jedinečná instance datové struktury, která je definována svou třídou. Objekt obsahuje datové členy (proměnné třídy a proměnné instance) i metody.
-
Přetěžování operátorů – Přiřazení více než jedné funkce určitému operátoru.
Vytváření tříd
Příkaz class vytváří definici nové třídy. Název třídy následuje bezprostředně za klíčovým slovem class následovaným dvojtečkou takto –
class ClassName: 'Optional class documentation string' class_suite
-
Třída má dokumentační řetězec, který je přístupný přes ClassName.__doc__.
-
Třída_suite se skládá ze všech složkových příkazů definujících členy třídy, datové atributy a funkce.
Příklad
Následuje příklad jednoduché třídy Pythonu –
class Employee: 'Common base class for all employees' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary
-
Proměnná empCount je proměnná třídy, jejíž hodnota je společná pro všechny instance této třídy. Lze k ní přistupovat jako k proměnné Employee.empCount zevnitř třídy i mimo ni.
-
První metoda __init__() je speciální metoda, které se říká konstruktor třídy nebo inicializační metoda, kterou Python volá při vytváření nové instance této třídy.
-
Další metody třídy deklarujete jako běžné funkce s tím rozdílem, že prvním argumentem každé metody je self. Python přidá argument self do seznamu za vás; při volání metod jej nemusíte uvádět.
Vytváření instancí objektů
Chcete-li vytvořit instance třídy, zavoláte třídu pomocí názvu třídy a předáte jí všechny argumenty, které její metoda __init__ přijímá.
"This would create first object of Employee class"emp1 = Employee("Zara", 2000)"This would create second object of Employee class"emp2 = Employee("Manni", 5000)
Přístup k atributům
K atributům objektu přistupujete pomocí operátoru tečka s objektem. K proměnné třídy by se přistupovalo pomocí názvu třídy takto –
emp1.displayEmployee()emp2.displayEmployee()print "Total Employee %d" % Employee.empCount
Nyní, když dáme všechny pojmy dohromady –
#!/usr/bin/pythonclass Employee: 'Common base class for all employees' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary"This would create first object of Employee class"emp1 = Employee("Zara", 2000)"This would create second object of Employee class"emp2 = Employee("Manni", 5000)emp1.displayEmployee()emp2.displayEmployee()print "Total Employee %d" % Employee.empCount
Při provedení výše uvedeného kódu vznikne následující výsledek –
Name : Zara ,Salary: 2000Name : Manni ,Salary: 5000Total Employee 2
Můžete přidávat, odebírat, nebo kdykoli upravovat atributy tříd a objektů –
emp1.age = 7 # Add an 'age' attribute.emp1.age = 8 # Modify 'age' attribute.del emp1.age # Delete 'age' attribute.
Místo běžných příkazů pro přístup k atributům můžete použít následující funkce –
-
Getattr(obj, name) – pro přístup k atributu objektu.
-
The hasattr(obj,name) – pro zjištění, zda atribut existuje, nebo ne.
-
The setattr(obj,name,value) – pro nastavení atributu. Pokud atribut neexistuje, pak by byl vytvořen.
-
The delattr(obj, name) – pro odstranění atributu.
hasattr(emp1, 'age') # Returns true if 'age' attribute existsgetattr(emp1, 'age') # Returns value of 'age' attributesetattr(emp1, 'age', 8) # Set attribute 'age' at 8delattr(empl, 'age') # Delete attribute 'age'
Vestavěné atributy třídy
Každá třída jazyka Python si uchovává následující vestavěné atributy a lze k nim přistupovat pomocí operátoru tečka jako k jakémukoli jinému atributu –
-
__dict__ – slovník obsahující jmenný prostor třídy.
-
__doc__ – řetězec dokumentace třídy nebo žádný, pokud není definován.
-
__name__ – Název třídy.
-
__module__ – Název modulu, ve kterém je třída definována. Tento atribut je v interaktivním režimu „__main__“.
-
__bases__ – Případně prázdný tuple obsahující základní třídy v pořadí jejich výskytu v seznamu základních tříd.
Pro výše uvedenou třídu zkusme přistupovat ke všem těmto atributům –
#!/usr/bin/pythonclass Employee: 'Common base class for all employees' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salaryprint "Employee.__doc__:", Employee.__doc__print "Employee.__name__:", Employee.__name__print "Employee.__module__:", Employee.__module__print "Employee.__bases__:", Employee.__bases__print "Employee.__dict__:", Employee.__dict__
Při spuštění výše uvedeného kódu dojde k následujícímu výsledku –
Employee.__doc__: Common base class for all employeesEmployee.__name__: EmployeeEmployee.__module__: __main__Employee.__bases__: ()Employee.__dict__: {'__module__': '__main__', 'displayCount':<function displayCount at 0xb7c84994>, 'empCount': 2, 'displayEmployee': <function displayEmployee at 0xb7c8441c>, '__doc__': 'Common base class for all employees', '__init__': <function __init__ at 0xb7c846bc>}
Zničení objektů (Garbage Collection)
Python automaticky odstraní nepotřebné objekty (vestavěné typy nebo instance tříd), aby uvolnil místo v paměti. Proces, kterým Python pravidelně získává zpět bloky paměti, které již nejsou používány, se označuje jako Garbage Collection (sběr odpadu).
Sběrač odpadu Pythonu běží během provádění programu a je spuštěn, když počet referencí na objekt dosáhne nuly. Počet referencí objektu se mění se změnou počtu aliasů, které na něj ukazují.
Počet referencí objektu se zvyšuje, když je mu přiřazeno nové jméno nebo je umístěn do kontejneru (seznamu, tuplu nebo slovníku). Počet referencí objektu se sníží, když je objekt odstraněn příkazem del, když je jeho reference znovu přiřazena nebo když se jeho reference dostane mimo obor. Když počet referencí objektu dosáhne nuly, Python jej automaticky sesbírá.
a = 40 # Create object <40>b = a # Increase ref. count of <40> c = # Increase ref. count of <40> del a # Decrease ref. count of <40>b = 100 # Decrease ref. count of <40> c = -1 # Decrease ref. count of <40>
Obvykle si nevšimnete, když garbage collector zničí osiřelou instanci a získá zpět její prostor. Třída však může implementovat speciální metodu __del__(), nazývanou destruktor, která se vyvolá, když má být instance zničena. Tato metoda může být použita k vyčištění všech nepaměťových prostředků, které instance používá.
Příklad
Tento destruktor __del__() vypíše jméno třídy instance, která má být zničena –
#!/usr/bin/pythonclass Point: def __init__( self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print class_name, "destroyed"pt1 = Point()pt2 = pt1pt3 = pt1print id(pt1), id(pt2), id(pt3) # prints the ids of the obejctsdel pt1del pt2del pt3
Při provedení výše uvedeného kódu vznikne následující výsledek –
3083401324 3083401324 3083401324Point destroyed
Poznámka – Ideální je definovat třídy v samostatném souboru a pak je importovat do hlavního souboru programu pomocí příkazu import.
Dědičnost tříd
Namísto toho, abyste začínali od nuly, můžete vytvořit třídu tak, že ji odvodíte od již existující třídy, a to tak, že za názvem nové třídy uvedete v závorce rodičovskou třídu.
Dětská třída zdědí atributy své rodičovské třídy a vy můžete tyto atributy používat, jako by byly definovány v dětské třídě. Podřízená třída může také přepisovat datové členy a metody z rodičovské třídy.
Syntaxe
Derivované třídy se deklarují podobně jako jejich rodičovské třídy; avšak za názvem třídy je uveden seznam základních tříd, od kterých má dědit –
class SubClassName (ParentClass1): 'Optional class documentation string' class_suite
Příklad
#!/usr/bin/pythonclass Parent: # define parent class parentAttr = 100 def __init__(self): print "Calling parent constructor" def parentMethod(self): print 'Calling parent method' def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print "Parent attribute :", Parent.parentAttrclass Child(Parent): # define child class def __init__(self): print "Calling child constructor" def childMethod(self): print 'Calling child method'c = Child() # instance of childc.childMethod() # child calls its methodc.parentMethod() # calls parent's methodc.setAttr(200) # again call parent's methodc.getAttr() # again call parent's method
Při provedení výše uvedeného kódu vznikne následující výsledek –
Calling child constructorCalling child methodCalling parent methodParent attribute : 200
Podobným způsobem můžete řídit třídu z více rodičovských tříd následujícím způsobem –
class A: # define your class A.....class B: # define your class B.....class C(A, B): # subclass of A and B.....
K ověření vztahu dvou tříd a instancí můžete použít funkce issubclass() nebo isinstance().
-
Boleanská funkce issubclass(sub, sup) vrací true, pokud je daná podtřída sub skutečně podtřídou nadtřídy sup.
-
Boleanská funkce isinstance(obj, Class) vrací true, pokud je obj instancí třídy Class nebo je instancí podtřídy třídy Class
Přepisování metod
Metody nadřazené třídy můžete vždy přepsat. Jedním z důvodů pro přepisování metod nadřazené třídy je to, že můžete chtít ve své podtřídě speciální nebo jinou funkčnost.
Příklad
#!/usr/bin/pythonclass Parent: # define parent class def myMethod(self): print 'Calling parent method'class Child(Parent): # define child class def myMethod(self): print 'Calling child method'c = Child() # instance of childc.myMethod() # child calls overridden method
Při provedení výše uvedeného kódu vznikne následující výsledek –
Calling child method
Přepisování metod
Následující tabulka uvádí některé obecné funkčnosti, které můžete přepisovat ve svých vlastních třídách –
Sr.č. 3. | Metoda, popis &Vzorové volání |
---|---|
1 |
__init__ ( self ) Konstruktor (s libovolnými nepovinnými argumenty) Vzorové volání : obj = className(args) |
2 |
__del__( self ) Destruktor, odstraní objekt Příklad volání : del obj |
3 |
__repr__( self ) Vyhodnocení reprezentace řetězce Vzorové volání : repr(obj) |
4 |
__str__( self ) Vytisknutelná reprezentace řetězce Vzorové volání : str(obj) |
5 |
__cmp__ ( self, x ) Porovnání objektů Vzorové volání : cmp(obj, x) |
Přetěžování operátorů
Předpokládejme, že jste vytvořili třídu Vector, která reprezentuje dvourozměrné vektory, co se stane, když k jejich sčítání použijete operátor plus? S největší pravděpodobností na vás Python zařve.
Můžete však ve své třídě definovat metodu __add__, která provede sčítání vektorů, a pak se operátor plus bude chovat podle očekávání –
Příklad
#!/usr/bin/pythonclass Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self,other): return Vector(self.a + other.a, self.b + other.b)v1 = Vector(2,10)v2 = Vector(5,-2)print v1 + v2
Při provedení výše uvedeného kódu vznikne následující výsledek –
Vector(7,8)
Skrývání dat
Atributy objektu mohou, ale nemusí být viditelné mimo definici třídy. Atributy je třeba pojmenovat předponou s dvojitým podtržítkem a tyto atributy pak nejsou pro cizí osoby přímo viditelné.
Příklad
#!/usr/bin/pythonclass JustCounter: __secretCount = 0 def count(self): self.__secretCount += 1 print self.__secretCountcounter = JustCounter()counter.count()counter.count()print counter.__secretCount
Při provedení výše uvedeného kódu vznikne následující výsledek –
12Traceback (most recent call last): File "test.py", line 12, in <module> print counter.__secretCountAttributeError: JustCounter instance has no attribute '__secretCount'
Python chrání tyto členy tím, že interně změní jejich název tak, aby obsahoval název třídy. K takovým atributům můžete přistupovat jako object._className__attrName. Pokud byste svůj poslední řádek nahradili následujícím, pak vám to bude fungovat –
.........................print counter._JustCounter__secretCount
Při spuštění výše uvedeného kódu vznikne následující výsledek –