[pyar] Duda: @property con __(set|get)attr__

Claudio Freire klaussfreire en gmail.com
Mie Feb 13 22:35:34 ART 2013


2013/2/13 Germán L. Osella Massa <gosella en gmail.com>:
> El 13 de febrero de 2013 17:46, Claudio Freire <klaussfreire en gmail.com>
> escribió:
>
>>
>> No, eso invoca al getter cuando se invoca al setter, lo que puede ser
>> bastante anti-intuitivo (y dependiendo de lo que haga el setter,
>> indeseado)
>
>
> Coincido con vos plenamente pero anti-intuitivo y todo, Python es así:


No, no es cosa de python, es cosa del código que chequea si tiene el atributo.

obj.atributo = 3

Sólo invoca

obj.__setattr__('atributo', 3)

No debería invocar

obj.__getattr__('atributo')

Pero el código primero sí lo hace.

El código segundo es interesante... no ideal, pero interesante. Creo
que una metaclase no sería tan mala, y de hecho sería mucho más
robusto (ej: se podría subclasear fácilmente):

>>> def with_attrs(name, bases, attrs):
...     attrset = set(attrs)
...     for base in bases:
...          attrset.update(set(dir(base)))
...     attrs['_attrs'] = frozenset(attrset)
...     return type(name, bases, attrs)
...
>>> class pdict(dict):
...     __metaclass__ = with_attrs
...     @property
...     def foo(self):
...         print("foo.getter")
...
...     @foo.setter
...     def foo(self, value):
...         print("foo.setter", value)
...
...     def __getattr__(self, name):
...         return self.get(name, None)
...
...     def __setattr__(self, name, value):
...         print("In pdict.__setattr__(self, %r, %r):" % (name, value))
...         if name in self._attrs:
...             print("\tsuper().__setattr__(%r, %r)" % (name, value))
...             super().__setattr__(name, value)
...         else:
...             print("\tself[%r] = %r" % (name, value))
...             self[name] = value


Pero me sigue oliendo mal tener que implementar la lógica esa en
setattr, y tener que agregar el atributo ese. Justamente una metaclase
podría "anotar" el setattr automágicamente:

>>> def with_attrs(name, bases, attrs):
...     attrset = set(attrs)
...     for base in bases:
...          attrset.update(set(dir(base)))
...     attrset = frozenset(attrset)
...     if attrset and '__setattr__' in attrs and
callable(attrs['__setattr__']):
...         _original_setattr = attrs['__setattr__']
...         def __setattr__(self, name, value):
...              if name in attrset:
...                  return super(cls, self).__setattr__(name, value)
...              else:
...                  return _original_setattr(self, name, value)
...         attrs['__setattr__'] = __setattr__
...     cls = type(name, bases, attrs)
...     return cls
...
>>> class pdict(dict):
...     __metaclass__ = with_attrs
...     @property
...     def foo(self):
...         print("foo.getter")
...
...     @foo.setter
...     def foo(self, value):
...         print("foo.setter", value)
...
...     def __getattr__(self, name):
...         return self.get(name, None)
...
...     def __setattr__(self, name, value):
...         print("In pdict.__setattr__(self, %r, %r):" % (name, value))
...
>>> d = pdict()
>>> d.foo
foo.getter
>>> d.foo = 3
('foo.setter', 3)
>>> d.bar
>>> d.bar = 4
In pdict.__setattr__(self, 'bar', 4):

Listo. Automagia.



More information about the pyar mailing list