¿Es posible usar la función property () con funciones decoradas con método de clase?
No.
Sin embargo, un método de clase es simplemente un método enlazado (una función parcial) en una clase accesible desde instancias de esa clase.
Dado que la instancia es una función de la clase y puede derivar la clase de la instancia, puede obtener el comportamiento deseado que desee de una propiedad de clase con property:
class Example(object):
_class_property = None
@property
def class_property(self):
return self._class_property
@class_property.setter
def class_property(self, value):
type(self)._class_property = value
@class_property.deleter
def class_property(self):
del type(self)._class_property
Este código se puede usar para probar, debe pasar sin generar ningún error:
ex1 = Example()
ex2 = Example()
ex1.class_property = None
ex2.class_property = 'Example'
assert ex1.class_property is ex2.class_property
del ex2.class_property
assert not hasattr(ex1, 'class_property')
Y tenga en cuenta que no necesitábamos metaclases en absoluto, y de todos modos no accede directamente a una metaclase a través de las instancias de sus clases.
escribiendo un @classpropertydecorador
En realidad, puede crear un classpropertydecorador en solo unas pocas líneas de código subclasificando property(está implementado en C, pero puede ver Python equivalente aquí ):
class classproperty(property):
def __get__(self, obj, objtype=None):
return super(classproperty, self).__get__(objtype)
def __set__(self, obj, value):
super(classproperty, self).__set__(type(obj), value)
def __delete__(self, obj):
super(classproperty, self).__delete__(type(obj))
Luego trate al decorador como si fuera un método de clase combinado con una propiedad:
class Foo(object):
_bar = 5
@classproperty
def bar(cls):
"""this is the bar attribute - each subclass of Foo gets its own.
Lookups should follow the method resolution order.
"""
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
@bar.deleter
def bar(cls):
del cls._bar
Y este código debería funcionar sin errores:
def main():
f = Foo()
print(f.bar)
f.bar = 4
print(f.bar)
del f.bar
try:
f.bar
except AttributeError:
pass
else:
raise RuntimeError('f.bar must have worked - inconceivable!')
help(f) # includes the Foo.bar help.
f.bar = 5
class Bar(Foo):
"a subclass of Foo, nothing more"
help(Bar) # includes the Foo.bar help!
b = Bar()
b.bar = 'baz'
print(b.bar) # prints baz
del b.bar
print(b.bar) # prints 5 - looked up from Foo!
if __name__ == '__main__':
main()
Pero no estoy seguro de lo bien aconsejado que sería. Un viejo artículo de la lista de correo sugiere que no debería funcionar.
Conseguir que la propiedad funcione en la clase:
La desventaja de lo anterior es que la "propiedad de clase" no es accesible desde la clase, porque simplemente sobrescribiría el descriptor de datos de la clase __dict__.
Sin embargo, podemos anular esto con una propiedad definida en la metaclase __dict__. Por ejemplo:
class MetaWithFooClassProperty(type):
@property
def foo(cls):
"""The foo property is a function of the class -
in this case, the trivial case of the identity function.
"""
return cls
Y luego una instancia de clase de la metaclase podría tener una propiedad que acceda a la propiedad de la clase utilizando el principio ya demostrado en las secciones anteriores:
class FooClassProperty(metaclass=MetaWithFooClassProperty):
@property
def foo(self):
"""access the class's property"""
return type(self).foo
Y ahora vemos tanto la instancia
>>> FooClassProperty().foo
<class '__main__.FooClassProperty'>
y la clase
>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>
tener acceso a la propiedad de la clase.