self, other

Transkrypt

self, other
Podstawy programowania
Python – wykład 10
Definiowanie metody inicjalizującej
class Nazwa_Klasy:
def __init__(self, param_list):
block
●
Python wywołuje tę metodę zaraz po tym, jak interpreter
utworzy egzemplarz klasy Nazwa_Klasy.
●
Parametr self odwołuje się do nowego egzemplarza.
●
Instrukcje w block mogą odwoływać się do zmiennych.
●
●
Lista param_list reprezentuje zero lub więcej parametrów
oddzielonych przecinkami.
block jest wciętym blokiem instrukcji.
Uwagi
●
●
Metoda __init__() jest umownie definiowana w klasie jako pierwsza.
self odnosi się do nowo utworzonego egzemplarza (w przypadku
innych metod self odnosi się do egzemplarza, którego metoda
została wywołana).
●
Metoda __init__() powinna zwracać None.
●
Instrukcja:
egz = Nazwa_Klasy()
jest wewnetrznie odpowiednikiem następującej:
Point.__init__(egz)
Przykład 1
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
pt1 = Point(3,9)
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3))
Usuwanie egzemplarza
●
●
●
●
●
Python w sposób automatyczny usuwa niewykorzystywane
(osierocone) egzemplarze przez proces odśmiecania i
zwolnienia zajmowanego obszaru pamięci.
Klasa może jednak poslużyć się specjalną metodą __del__(),
która zwana jest destruktorem.
Jest ona wywoływana wtedy, gdy dany egzemplarz ma być
usunięty.
Metoda ta może być wykorzystana do oczyszczania wszelkich
zasobów pamięci używanych przez egzemplarz.
Metoda ta jest rzadko stosowana w praktyce, ponieważ nie
wiadomo czy będzie wywołana w okreslonym odstępie czsu
(może zdarzyć się, że nie będzie wywołana wcale).
Przykład 2
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __del__(self):
nazwa = self.__class__.__name__
print(nazwa, 'zniszczono')
pt1 = Point(3,9)
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3))
del(pt1); del(pt2); del pt3
Tworzenie nieformalnej reprezentacji
łańcuchowej
def __str__(self):
block
●
●
Python wywołuje tę metodę wtedy, gdy egzemplarz jest używany jako
argument funkcji str() lub print.
Wielkość self dotyczy egzemplarza, dla którego jest wywoływana
metod __str__().
●
Zwracana wartość musi być obiektem łańcuchowym.
●
Wielkość block jest wciętym blokiem instrukcji.
Tworzenie formalnej reprezentacji
łańcuchowej
def __repr__(self):
block
●
●
Python wywołuje tę metodę wtedy, gdy egzemplarz jest używany jako
argument funkcji repr(), albo gdy jest on otoczony odwrotnymi
apostrofami.
Wielkość self dotyczy egzemplarza, dla którego jest wywoływana
metod __repr__().
●
Zwracana wartość musi być obiektem łańcuchowym.
●
Wielkość block jest wciętym blokiem instrukcji.
UWAGI
●
Formalna reprezentacja łańcuchowa egzemplarza jest używana
głównie przy wyszukiwaniu błędów w fazie uruchamiania programu,
zatem ważne jest, by metoda __repr__() zwracała łańcuch
zawierający pełną i jednoznaczną informację.
●
Jeśli nie jest możliwe uzyskanie poprawnego wyrażenia Pythona za
pomocą metody __repr__() powinna ona zwracać łańuch w postaci
''<...opis...>''. Opis powinien zawierać szczegółową informację
o egzemplarzu.
Przykład 3
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __str__(self):
return '(%g,%g)' %(self.x, self.y)
def __repr__(self):
return 'Point(%s,%s)' %(self.x, self.y)
...
print(str(pt1))
print(repr(pt1))
Określanie prawdziwości egzemplarza
def __nonzero__(self):
block
●
●
Python wywołuje tę metodę wtedy, gdy egzemplarz zostanie
użyty w wyrażeniu logicznym.
Wielkość self dotyczy egzemplarza, dla którego jest
wywoływana metod __nonzero__().
●
Zwracana wartość musi być 1 (prawda) lub 0 (fałsz).
●
Wielkość block jest wciętym blokiem instrukcji.
●
Jeśli metoda nie jest zdefiniowana, Python wywołuje metodę
__len__(). Jeśli klasa nie definiuje obydwu metod, wszystkie
egzemplarze będą traktowane jako prawdziwe.
Przykład 4
class Point:
...
def __nonzero__(self):
if self.x == 0 and self.y == 0:
return 0
else:
return 1
...
for pt in [pt1, pt2, pt3]:
if pt:
print(pt, 'is true')
else:
print(pt, 'is false')
Porównywanie egzemplarzy
def __comp__(self, other):
block
●
●
●
●
●
__comp__() jest jedną z metod z tabeli 1.
Python wywołuje tę metodę wtedy, gdy egzemplarz zostanie użyty
w wyrażeniu porównującym.
Wielkość self dotyczy egzemplarza, dla którego jest
wywoływana metod __comp__(), other jest egzemplarzem, z
którym dany egzemplarz jest porównywany.
Zwracana wartość może być dowolną wartością, lecz w kontekście
logicznym powinna to być wartość logiczna 1 (prawda) lub 0
(fałsz); w przeciwny przypadku wystapi wyjątek TypeError.
Wielkość block jest wciętym blokiem instrukcji.
Tabela 1
Metoda
__lt__(self,other)
__le__(self,other)
__gt__(self,other)
__ge__(self,other)
__eq__(self,other)
__ne__(self,other)
Wynik
self <
self <=
self >
self >=
self ==
self !=
other
other
other
other
other
other
Przykład 5
from math import hypot
class Point: ...
def __lt__(self, other):
return hypot(self.x,self.y) < hypot(other.x,other.y)
def __le__(self, other):
return hypot(self.x,self.y) <= hypot(other.x,other.y)
def __gt__(self, other):
return hypot(self.x,self.y) > hypot(other.x,other.y)
def __ge__(self, other):
return hypot(self.x,self.y) >= hypot(other.x,other.y)
def __eq__(self, other):
return hypot(self.x,self.y) == hypot(other.x,other.y)
def __ne__(self, other):
return hypot(self.x,self.y) != hypot(other.x,other.y)
UWAGI
●
●
Podane metody porównują egzemplarze
klasy Point korzystając z odległości
geometrycznej od początku układu
współrzędnych (0,0).
Funkcja math.hypot(x,y) oblicza
odległość zgodnie z regułami geometrii
euklidesowej, która równa się
math.sqrt(x*x+y*y).
Metody operatorów dwustronnych
Metoda
Wynik
__add__(self, other)
__sub__(self, other)
__mul__(self, other)
__div__(self, other)
__mod__(self, other)
__divmod__(self, other)
__pow__(self, other)
__lshift__(self, other)
__rshift__(self, other)
__and__(self, other)
__or__(self, other)
__xor__(self, other)
self + other
self - other
self * other
self / other
self % other
divmod(self, other)
self ** other
self << other
self >> other
self & other
self | other
self ^ other
Metody przypisań przyrostowych
Metoda
Wynik
__iadd__(self, other)
__isub__(self, other)
__imul__(self, other)
__idiv__(self, other)
__imod__(self, other)
__ipow__(self, other)
__ilshift__(self, other)
__irshift__(self, other)
__iand__(self, other)
__ior__(self, other)
__ixor__(self, other)
self
self
self
self
self
self
self
self
self
self
self
+= other
-= other
*= other
/= other
%= other
**= other
<<= other
>>= other
&= other
|= other
^= other
Przykład 6
class Point: ...
def __add__(self, other):
if hasattr(other, '__class__') and other.__class__ is Point:
return Point(self.x + other.x, self.y + other.y)
else:
return Point(self.x + other, self.y + other)
def __pow__(self, other):
return Point(self.x ** other,self.y ** other)
pt1 = Point(0,0)
pt2 = Point(2,2)
pt = pt1 + pt2
print(pt)
print(pt2 + 10)
print(pt2**2)
Dziedziczenie
class NazwaKlasyPotomnej(NazwaKlasyBazowej):
<instrukcja_1>
.
.
.
<instrukcja_N>
UWAGI
●
●
●
Nazwa NazwaKlasyBazowej musi być zdefiniowana
w zasięgu zawierającym definicję klasy pochodnej. Zamiast
nazwy klasy bazowej dopuszcza się również wyrażenie.
Jeśli poszukiwany atrybut (lub metoda) nie jest znajdowany
w klasie, poszukiwany jest w klasie bazowej. Ta zasada
stosowana jest rekurencyjnie w przypadku, gdy klasa
bazowa jest pochodną innej.
Klasy pochodne mogą przesłaniać metody ich klas
bazowych. (Uwaga dla programistów C++: wszystkie metody
w Pythonie zachowują sie jak wirtualne.)
Dziedziczenie wielorakie
class NazwaKlasyPochodnej(Bazowa1, Bazowa2, ...):
<instrukcja_1>
.
.
.
<instrukcja_N>
UWAGI
●
●
Zasada ,,najpierw w głąb, potem na prawo''. Jeśli atrybut nie
zostanie znaleziony w klasie NazwaKlasyPochodnej, zostanie
poszukany w Bazowa1, a potem (rekurencyjnie) w klasach
bazowych klasy Bazowa1 i jeśli tam nie zostanie znaleziony,
poszukiwanie zostanie przeniesione do klasy Bazowa2.
Nieograniczone użycie dziedziczenia wielorakiego może w
oczywisty sposób stać się koszmarem w procesie pielęgnacji
oprogramowania, zwłaszcza że w Pythonie unikanie konfliktów
nazw opiera się na umowie. Jednym z bardziej znanych
przykładów problemu z dziedziczeniem wielorakim jest sytuacja
gdy dwie klasy bazowe dziedziczą z tej samej klasy-przodka. Nie
wiadomo jak bardzo taka semantyka jest użyteczna w połączeniu z
faktem, że mamy pojedynczą kopię ,,zmiennych konkretu'' lub
atrybutów danych wspólnej klasy bazowej.

Podobne dokumenty