Get/Set a Property

Dotaz

Celkem mě zaujala ta debata kolem getters and setters. Osobně jsem zastánce používáni téhle metody. Čistě protože si myslím, ze o nastavovaní/vracení atributu by se mela starat třída. Četl jsem ten text tady: http://dirtsimple.org/2004/12/python-is-not-java.html

A zaujala me věta: In Python, this is silly, because you can start with a normal attribute and change your mind at any time, without affecting any clients of thé class.

Asi je to jen mou neznalosti, ale přeci když budu důsledně používat get a set metody, tak si při případné změně atributu ulehčím práci, jelikož taková změna bude znamenat editaci pouze samotné třídy a nebudu muset procházet cely kód a hledat kde všude se na třídu odvolávám.

Příklad

class Opicka:
    def __init__(self):
        self.nick = ""
    def setNick(self, nick):
        self.nick = nick
    def getNick(self):
        return self.nick

orangutan = Opicka()
orangutan.setNick("Tonda")
print orangutan.getNick()

Odpověď

No to zrovna u jazyků, které mají property je zbytečnost, ne? Vždycky jsem považoval gettery/settery za nouzové východisko u jazyků, které nemají v syntaxi jazyka property - jako třeba Java, nebo C++.

Pokud používáš get/set metody explicitně, tak děláš práci úplné zbytečnou. Jednoduchý příklad:

# Třída
class C(object):
    def __init__(self):
        self.x = 0

    def magic(self):
        self.x = self.x * 10

# Program
pom = C()
pom.x = 11
print pom.x
pom.magic()
print pom.x

Vše je OK, ale z nějakého obskurního důvodu je najednou do x místo nuly potřeba ukládat dvojnásobek kladných přiřazovaných hodnot, místo záporného čísla nulu.

Není nic jednoduššího, než změnit atribut x na property x. Property v Pythonu funguje tak, ze u vytvořené property nadefinuješ, jaká funkce se vola při přístupu k property pro čtení (getter) a při přístupu k property pro zápis (setter):

class C(object):
    def __init__(self):
        self._x = 0;

    def magic(self):
        self.x = self.x * 10

    def get_x(self):
        return(self._x)

    def set_x(self, value):
        if value >= 0:
             self._x = value * 2
        else:
             self._x = 0

    x = property(get_x, set_x)

# Nyní mohu bez problému provést původní program:
pom = C()
pom.x = 11
print pom.x
pom.magic()
print pom.x

a mám i požadované chovaní. Všimni si, ze požadovaná změna chovaní se projevila i ve funkci C.magic() která k property x taktéž přistupuje. Pokud by ses tomuto chtěl vyhnout, tak stačí změnit self.x na self._x

Čili tím, ze explicitně voláš get_x a set_x tak sice neděláš chybu, ale děláš zbytečnou práci a navíc to vypadá děsně.