TI/Wstęp do programowania obiektowego/Cwiczenia
Spis treści
Ćwiczenia
Implementacja prostej hierarchii klas
Zaimplementuj mini hierarchię klas zamodelowaną na poniższym obrazku. Pamiętaj, żeby przy dziedziczeniu i przedefiniowywaniu metod korzystać z metody super -- wyjaśnij, czemu takie wyjście jest lepsze niż bezpośrednie odwoływanie się do metody z nadklasy? Pamiętaj o tym, aby odpowiednie metody atrybuty implementować jako metody / atrybuty odpowiednio klasy / obiektu. Sprawdź, czy zmiana wartości atrybutu klasowego w podklasie jest widoczna w nadklasie, i na odwrót. (Podkreślenie oznacza atrybut/metodę klasy, bez podkreślenia -- obiektu)
- Rozszerz którąś z klas o metodę __call__(), spróbuj wymyśleć "przydatny" sposób jej działania i je zaprezentuj.
- Rozszerz klasę A o metodę __dell__(), która będzie wypisywać napis "umieram".
Co i dlaczego się stanie, gdy zrobisz:
a = A()
# z dokładnością do tego, że być może Twoja implementacja
# potrzebuje parametrów w konstruktorze
b = a
c = a
del a
del b
del c
A kto i dlaczego krzyczy "umieram" w następującej sytuacji:
a = A()
b = A()
b = a
Przykładziki na różne zagadnienia
Atrybuty vs metody
Niech definicja klasy będzie następująca:
class SztucznyPrzyklad:
def __init__(self):
self.atrybut1 = None
def metoda1(self):
return "jestem metoda"
Co i dlaczego będzie na zmiennej sp:
>>> sp = SztucznyPrzyklad
#podpowiedz:
>>> type(sp)
<type 'classobj'>
A teraz?:
>>> sp = SztucznyPrzyklad()
>>> sp = SztucznyPrzyklad()
>>> type(sp)
<type 'instance'>
Co będzie na m1?
>>> m1 = sp.metoda1
>>> type(m1)
<type 'instancemethod'>
>>> m1 = sp.metoda1()
>>> type(m1)
<type 'str'>
A jak się ma sprawa z atrybutami?
>>> a1 = sp.atrybut1()
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
a1 = sp.atrybut1()
TypeError: 'NoneType' object is not callable
>>> a1 = sp.atrybut1
>>> type(a1)
<type 'NoneType'>
Rozszerzmy definicję z przykładu:
class SztucznyPrzyklad:
def __init__(self, parametr1, parametr2):
self.atrybut1 = parametr1
self.atrybut2 = parametr2
def metoda1(self):
return "jestem metoda"
ff = 5
def f():
return 10
Zauważmy, że w takiej sytuacji:
sp = SztucznyPrzyklad(f)
a = sp.atrybut1()
"wywołanie atrybutu" jest poprawne, ponieważ atrybut jest funkcją. Naley jednak to robić świadomie, i uważać na to, czy chcieliśmy się odwołać do obiektu funkcja, czy też do wyniku działania funkcji.
Poniższe odwołanie jest wciąż poprawne, tylko zwraca nam obiekt typu funkcja
a = sp.atrybut1
Metody obiektów i klasowe
class Test(object):
def method_one(self):
print "Called method_one"
def method_two():
print "Called method_two"
a_test = Test()
a_test.method_one()
a_test.method_two()
Wyjaśnij, czemu w powyższym przykładzie nie działa wywołanie a_test.method_two(). Jak można to poprawić?
Najprostszy dekorator
Niech będzie dana zdefiniwana poza klasą funkcja f:
def f(g):
g.pole = True
return g
Do ciała klasy A z pierwszego ćwiczenia dodaj następujący fragment:
f(m1)
Zauważ, że w tak zdefiniowanej klasie A, jej metoda m1 ma atrybut pole, ustawiony na True. Spróbuj osiągnąć ten sam efekt
- zamiast wywołania f(m1) używając konstrukcji z "@"
- zamiast wywołania f(m1) tworząc klasę f robiącą w metodzie __call__ to samo i używając konstrukcji z "@"
Wymyśl przydatne zastosowanie takiej techniki.
Szukanie błędów
Znajdź, wyjaśnij i popraw błędy w poniższych kawałkach kodu. Podaj treść komunikatów o błędach, które wyświetla python -- wyjaśnij tę treść.
class DecoratorTest:
def __init__(self):
pass
def __call__(self, f):
f.pole = 0
return f()
@DecoratorTest
def f():
return 42
f()
print f.pole
class logged(object):
def __init__(self, funkcja):
self.funkcja = funkcja
def __call__(self, *args, **kwargs):
print "wywoanie", self.funkcja.__name__
x = self.funkcja(*args, **kwargs)
print self.funkcja.__name__, "zwrcia", x
return x
@logged()
def f():
return g() + g()
@logged()
def g():
return 1
f()