decorators - Python class @property: usa setter pero evade getter?
python property decorator (2)
En las clases de python, @property es un lindo decorador que evita el uso de funciones explícitas de setter y getter. Sin embargo, tiene un costo de una sobrecarga de 2-5 veces la de una función de clase "clásica". En mi caso, esto está bastante bien en el caso de establecer una propiedad, donde la sobrecarga es insignificante en comparación con el procesamiento que se debe realizar al configurar.
Sin embargo, no necesito procesamiento al obtener la propiedad. Siempre es solo "return self.property". ¿Existe una forma elegante de usar el colocador pero sin utilizar el captador, sin la necesidad de utilizar una variable interna diferente?
Solo para ilustrar, la clase siguiente tiene la propiedad "var" que se refiere a la variable interna "_var". Se necesita más tiempo para llamar a "var" que a "_var", pero sería bueno si los desarrolladores y los usuarios pudieran simplemente usar "var" sin tener que hacer un seguimiento de "_var" también.
class MyClass(object):
def __init__(self):
self._var = None
# the property "var". First the getter, then the setter
@property
def var(self):
return self._var
@var.setter
def var(self, newValue):
self._var = newValue
#... and a lot of other stuff here
# Use "var" a lot! How to avoid the overhead of the getter and not to call self._var!
def useAttribute(self):
for i in xrange(100000):
self.var == ''something''
Para aquellos interesados, en mi PC llamar a "var" toma 204 ns en promedio mientras que llamar a "_var" toma 44 ns en promedio.
No use una property
en este caso. Un objeto de property
es un descriptor de datos, lo que significa que cualquier acceso a instance.var
invocará ese descriptor y Python nunca buscará un atributo en la instancia en sí.
Tiene dos opciones: use el .__setattr__()
gancho o .__setattr__()
un descriptor que solo implemente .__set__
.
Usando el .__setattr__()
gancho
class MyClass(object):
var = ''foo''
def __setattr__(self, name, value):
if name == ''var'':
print "Setting var!"
# do something with `value` here, like you would in a
# setter.
value = ''Set to '' + value
super(MyClass, self).__setattr__(name, value)
Ahora las búsquedas normales de atributos se usan cuando se lee .var
pero cuando se asigna a .var
se invoca el método __setattr__
, lo que le permite interceptar el value
y ajustarlo según sea necesario.
Manifestación:
>>> mc = MyClass()
>>> mc.var
''foo''
>>> mc.var = ''bar''
Setting var!
>>> mc.var
''Set to bar''
Un descriptor setter
Un descriptor setter solo interceptaría la asignación de variable:
class SetterProperty(object):
def __init__(self, func, doc=None):
self.func = func
self.__doc__ = doc if doc is not None else func.__doc__
def __set__(self, obj, value):
return self.func(obj, value)
class Foo(object):
@SetterProperty
def var(self, value):
print ''Setting var!''
self.__dict__[''var''] = value
Tenga en cuenta cómo tenemos que asignar el atributo instancia .__dict__
para evitar invocar al colocador de nuevo.
Manifestación:
>>> f = Foo()
>>> f.var = ''spam''
Setting var!
>>> f.var = ''ham''
Setting var!
>>> f.var
''ham''
>>> f.var = ''biggles''
Setting var!
>>> f.var
''biggles''
property
python docs: https://docs.python.org/2/howto/descriptor.html#properties
class MyClass(object):
def __init__(self):
self._var = None
# only setter
def var(self, newValue):
self._var = newValue
var = property(None, var)
c = MyClass()
c.var = 3
print (''ok'')
print (c.var)
salida:
ok
Traceback (most recent call last):
File "Untitled.py", line 15, in <module>
print c.var
AttributeError: unreadable attribute