[python] Getters and Setters.

Filip Štědronský regnarg na seznam.cz
Sobota Červen 21 20:13:21 CEST 2008


On So, čen 21, 2008 at 07:20:22 +0200, David Michal wrote:
> Ta promena x je pak promenou tridy, nebo instance? Co se stane kdyz
> upravim __init__ takto:
> def __init__(self):
>     self.x = 0
>     self.x = property(self.get_x, self.set_x)

Property je určena k tomu, aby byla uložena v proměnné
třídy, i když slouží jako proměnná instance. Efekty v
jiném případě by mohly být nepředvídatelné. Chování
property() bylo takhle navrženo naschvál, aby připomínalo
něco jako "deklaraci" v rámci definice třídy. Ale samotná
vlastnost reprezentuje hodnotu vztahující se k instanci.

Vzhledem ke složité sémantice ohledně parametru self si
nejsem jist, zda by bylo property možno použít jinak, než
jak bylo zamýšleno, protože parametry funkce property()
předávané v těle třídy jsou ještě holé funkce,
očekávající explicitní parametr self a ne "magické"
metody instance.

Property vytváří tzv. deskriptor, tedy objekt s metodami
__get__, __set__, etc. Tyto metody teprve dostanou jako
parametr aktuální instanci (automaticky, když Python
zjistí, že jde o deskriptor) a zavolají holou funkci s
parametrem této instance, případně u setteru nové
hodnoty. Pro funkci samotnou je to nerozeznatelné od
použití jako instanční metody: self dostane, parametry
dostane, tak si nemá co stěžovat.

Z uvedeného jasně vyplývá, že se nemusíme omezovat na
property. Můžeme si napsat vlastní deskriptory, stačí,
aby implementovali __get__, příp. __set__. Já osobně
doporučuji můj deskriptor prop(), který neuchovává odkaz
na funkci, ale její název, takže může být předefinována
v podtřídě, aniž by se musela předefinovat vlastnost
samotná.

Kód přikládám také pro představu, jak deskriptory vlastně
vypadají. Parametry __get__ jsou instance (None při použití
jako proměnná třídy) a třída objektu, jehož atributem
deskriptor je. U ostatních netřeba vysvětlovat. Magické
chování deskriptorů funguje jen v rámci objektů, tzn. v
obyčejné proměnné uvidíme vždy deskriptor samotný.

class prop(object):
    """A replacement of `property' that work correctly with subclasses
and manages that by name-based remembering of accessor methods (aka
getters and setters) in contrast to property's method object storing.
If the accessors are redefined in a subclass, the prop object can be
left intact, whereas a property requires to be redefined each time an
accessor is overriden."""
    def __init__(self,fget=None,fset=None,fdel=None):
        if fget is None:
            self.fget=None
        else:
            self.fget=fget.__name__
        if fset is None:
            self.fset=None
        else:
            self.fset=fset.__name__
        if fdel is None:
            self.fdel=None
        else:
            self.fdel=fdel.__name__
    def __get__(self,i,o):
        if self.fget is None: return
        if i is None:
            return getattr(o,self.fget)()
        else:
            return getattr(i,self.fget)()
    def __set__(self,i,v):
        if self.fset is None: return
        getattr(i,self.fset)(v)
    def __delete__(self,i):
        if self.fdel is None: return
        getattr(i,self.fdel)()

Pokud definujeme:

class A(object):
    def myget(self):
        return 0
    a=property(myget)
    b=prop(myget)
class B(A):
    def myget(self):
        return 1

Dostáváme:
>>> b=B()
>>> b.a
0
>>> b.b
1

Doufám, že jsem to vysvětlil dost jasně a neudělal moc
chyb (případně nechť mě opraví jiní). 
Přeji hezký zbytek večera,
regnarg
    
-- 
regnarg --- http://rg.pretel.cz -- JID: regnarg na jabber.cz
V péči o štěstí druhých nacházíme své vlastní.   --Platón
Přátelství může trvat jedině mezi dobrými lidmi. --Cicero



Další informace o konferenci Python