No olvides que en la informática un error casi siempere puede tener más de una soluciones, de igual modo nosotros enseñamos lo más óptimo y mejor.
Solución:
los property()
función devuelve un objeto descriptor especial:
>>> property()
Es este objeto el que tiene extra métodos:
>>> property().getter
>>> property().setter
>>> property().deleter
Estos actúan como decoradores. también. Devuelven un nuevo objeto de propiedad:
>>> property().getter(None)
esa es una copia del objeto anterior, pero con una de las funciones reemplazadas.
Recuerda, que el @decorator
la sintaxis es simplemente azúcar sintáctica; la sintaxis:
@property
def foo(self): return self._foo
realmente significa lo mismo que
def foo(self): return self._foo
foo = property(foo)
entonces foo
la función es reemplazada por property(foo)
, que vimos arriba es un objeto especial. Entonces cuando usas @foo.setter()
lo que estás haciendo es llamar así property().setter
El método que le mostré anteriormente, que devuelve una nueva copia de la propiedad, pero esta vez con la función setter reemplazada por el método decorado.
La siguiente secuencia también crea una propiedad completa mediante el uso de esos métodos de decorador.
Primero creamos algunas funciones y un property
objeto con solo un getter:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to !r!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
A continuación usamos el .setter()
método para agregar un setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
Por último, agregamos un eliminador con el .deleter()
método:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
Por último, pero no menos importante, el property
objeto actúa como un objeto descriptor, por lo que tiene .__get__()
, .__set__()
y .__delete__()
métodos para enganchar a la instancia attribute obteniendo, configurando y eliminando:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
El Descriptor Howto incluye una implementación de muestra de Python puro del property()
escribe:
class Property: "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
La documentación dice que es solo un atajo para crear propiedades de solo lectura. Entonces
@property
def x(self):
return self._x
es equivalente a
def getx(self):
return self._x
x = property(getx)
Aquí hay un ejemplo mínimo de cómo @property
puede ser implementado:
class Thing:
def __init__(self, my_word):
self._word = my_word
@property
def word(self):
return self._word
>>> print( Thing('ok').word )
'ok'
De lo contrario word
sigue siendo un método en lugar de una propiedad.
class Thing:
def __init__(self, my_word):
self._word = my_word
def word(self):
return self._word
>>> print( Thing('ok').word() )
'ok'
Si para ti ha sido de ayuda nuestro post, sería de mucha ayuda si lo compartes con más entusiastas de la programación así nos ayudas a dar difusión a nuestro contenido.