Speciální metody sloužící k implementaci podpory pro vestavěné funkce Pythonu a jinou rozšiřující funkcionalitu (např. __init__ jakožto konstruktor). Přehled dunder metod je dostupný zde.
String reprezentace
Implementací dunder metod __repr__ a __str__ implementujeme chování funkcí repr() a str()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_creditsdef__repr__(self):returnf"CreditAccount({self.owner}, {self.balance})"def__str__(self):returnself.__repr__()
__bool__# chování funkce bool()
__complex__# chování funkce complex()
__int__# chování funkce int()
__float__# chování funkce float()
__hash__# chování funkce hash()
Následující příklad dále implementuje dunder metody __bool__ a __int__.
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_creditsdef__repr__(self):returnf"CreditAccount({self.owner}, {self.balance})"def__str__(self):returnself.__repr__()def__bool__(self):returnbool(self.balance)def__int__(self):returnint(self.balance)
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_creditsdef__repr__(self):returnf"CreditAccount({self.owner}, {self.balance})"def__str__(self):returnself.__repr__()def__bool__(self):returnbool(self.balance)def__int__(self):returnint(self.balance)def__lt__(self,other):ifisinstance(other,CreditAccount):returnself.balance<other.balancereturnNotImplemented
__add__# chování +
__sub__# chování -
__mul__# chování *
__truediv__# chování /
__floordiv__# chování //
__mod__# chování %
__divmod__# chování divmod()
__pow__# chování ** nebo pow()
__round__# chování round()
Následující příklad dále implementuje dunder metodu __add__. V případě, že pro nějaký typ nejsou dunder metody implementovány, je nutné vracet konstantu NotImplemented.
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_creditsdef__repr__(self):returnf"CreditAccount({self.owner}, {self.balance})"def__str__(self):returnself.__repr__()def__bool__(self):returnbool(self.balance)def__int__(self):returnint(self.balance)def__lt__(self,other):ifisinstance(other,CreditAccount):returnself.balance<other.balancereturnNotImplementeddef__add__(self,other):ifisinstance(other,CreditAccount):returnself.balance+other.balancereturnNotImplemented
Kombinované operátory přiřazení s aritmetickými operacemi
Je běžné, ne však nutné, aby výsledná metoda vracela self.
1
2
3
4
5
6
7
__iadd__# chování +=
__isub__# chování -=
__imul__# chování *=
__itruediv__# chování /=
__ifloordiv__# chování //=
__imod__# chování %=
__ipow__# chování **=
Následující příklad dále implementuje dunder metodu __iadd__. V případě, že pro nějaký typ nejsou dunder metody implementovány, je nutné vracet konstantu NotImplemented.
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_creditsdef__repr__(self):returnf"CreditAccount({self.owner}, {self.balance})"def__str__(self):returnself.__repr__()def__bool__(self):returnbool(self.balance)def__int__(self):returnint(self.balance)def__lt__(self,other):ifisinstance(other,CreditAccount):returnself.balance<other.balancereturnNotImplementeddef__add__(self,other):ifisinstance(other,CreditAccount):returnself.balance+other.balancereturnNotImplementeddef__iadd__(self,value):ifisinstance(value,int):self.balance+=valuereturnselfreturnNotImplemented
__invert__# chování ~
__lshift__# chování <<
__rshift__# chování >>
__and__# chování &
__or__# chování |
__xor__# chování ^
Emulace kolekcí
Důležité, dostaneme se k nim později.
1
2
3
4
5
6
__index__# chování převodu na integer například při slicingu
__len__# chování len()
__getitem__# chování x[20]
__setitem__# chování x[20] = 2
__delitem__# chování del x[20]
__contains__# chování in
Použití implementovaných dunder metod
Implementovanou funkcionalitu můžeme použít rovněž v rámci třídy.
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_creditsdef__repr__(self):returnf"CreditAccount({self.owner}, {self.balance})"def__str__(self):returnself.__repr__()def__bool__(self):returnbool(self.balance)def__int__(self):returnint(self.balance)def__lt__(self,other):ifisinstance(other,CreditAccount):returnself.balance<other.balancereturnNotImplementeddef__add__(self,other):ifisinstance(other,CreditAccount):returnself.balance+other.balancereturnNotImplementeddef__iadd__(self,value):ifisinstance(value,int):self.balance+=valuereturnselfreturnNotImplementeddef__isub__(self,value):ifisinstance(value,int):self.balance-=valuereturnselfreturnNotImplementeddeftransfer_to(self,other,value):"""Transfer credit into another credit account. Negative balance is allowed.
Args:
other: Target of credit transfer.
value: Amount of credit to be transfered.
"""self-=valueother+=value
Dunder metody představují elegantní řešení pro implementaci podpory vestavěných funkcí. Většinou je tedy lepší využívat implementace těchto metod pro podporu len() než implementování vlastní metody object.length().
Protipříkladem je pomyslná třída Vector ve které chceme implementovat výpočet délky vektoru. Pozor, použití len() na instanci třídy Vector není vhodné, funkci len() používáme v kontextu kolekcí pro zjištění počtu prvků. U třídy Vector požadujeme jinou sémantiku. V takovém případě je tedy vhodnější zvolit implementaci metody Vector.length(), rovněž jsem se setkal s implementace dunder metody __abs__() a následné používání funkce abs().
Dekorátory ve třídách
@property
V jazyce Python nepoužíváme klasické (například Javovské) gettery, settery (tedy metody s názvy get_temperature a set_temperature). To plyne z vlastnosti veřejné dostupnosti všech hodnot objektu (narozdíl od jazyků jako je třeba Java). Vraťme se k jednoduchému příkladu třídy CreditAccount. Vlastnost CreditAccount.balance je dostupná pomoci tečkového operátoru.
1
2
3
4
5
6
7
8
9
10
11
12
13
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_credits
# špatné, ale bohužel časté řešení
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=0self.set_balance(initial_credits)defset_balance(self,new_balance):ifnew_balance<0:raiseValueError("Balance cannot be negative number!")self.balance=new_balance
1
2
3
4
5
6
7
8
9
fromcredit_accountimportCreditAccountcredit_account=CreditAccount("Lukas Novak",0)credit_account.set_balance(0)# negativní hodnotu balance můžeme stále nastavit
credit_account.balance=-100
Pro příklady kdy chceme modifikovat chování přístupu k vlastnosti třídy je nutné použít dekorátor @property.
# správně
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself._balance=0self.balance=initial_credits@propertydefbalance(self):returnself._balance@balance.setterdefbalance(self,new_balance):"""Sets new value of balance, new value cannot be negative."""ifnew_balance<0:raiseValueError("Balance cannot be negative number!")self._balance=new_balance@balance.deleterdefbalance(self):self._balance=0
Nejprve se podívejme na dekorátor @classmethod. Tento dekorátor lze použít v situaci kdy je nutné metodám předat odkaz na celou třídu. Demonstrovat jej můžeme na příkladu metod from_*, tedy metod které umí vytvořit instanci třídy různými způsoby.
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_credits@classmethoddeffrom_csv(cls,input_string,separator=","):"""Creates CreditAccount class from csv string"""owner,initial_credits=input_string.split(separator)returncls(owner,int(initial_credits))
classCreditAccount:"""Account with stored credits."""def__init__(self,owner,initial_credits=0):"""Creates credit account with given owner and initial credits.
Args:
owner: owner of the account
initial_credits (optional): credit balance. Defaults to 0.
"""self.owner=ownerself.balance=initial_credits@staticmethoddefcredit_to_money(credit,exchange_rate):"""Calculates money value of credits.
Args:
credit: amount of credits
exchange_rate: how many money per one credit
Returns: money value
"""returncredit*exchange_rate
1
2
3
4
5
6
7
8
9
fromcredit_accountimportCreditAccount# dostupné z třídy
CreditAccount.credit_to_money(100,20)credit_account=CreditAccount("Lukas Novak",200)# dostupné rovněž z objektu
credit_account.credit_to_money(100,20)
Pořadí definic metod
Neexistuje žádný jeden správný způsob v jakém pořadí metody třídy definovat. Populární možnost je následující:
Při řešení úloh nepoužívejte pokročilejší funkcionalitu jazyka která nebyla ještě představena! Takové úkoly budou vráceny na přepracování bez ohledu na jejich funkčnost.