TI/Wstęp do programowania obiektowego/Cwiczenia

Z Brain-wiki

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

Obiekty diagram.png

  • 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()