Est-il possible d'utiliser la fonction property () avec des fonctions décorées par classmethod?
Non.
Cependant, une méthode de classe est simplement une méthode liée (une fonction partielle) sur une classe accessible à partir des instances de cette classe.
Puisque l'instance est une fonction de la classe et que vous pouvez dériver la classe de l'instance, vous pouvez obtenir le comportement souhaité d'une propriété de classe avec 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
Ce code peut être utilisé pour tester - il devrait réussir sans générer d'erreurs:
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')
Et notez que nous n'avons pas du tout besoin de métaclasses - et que vous n'accédez pas directement à une métaclasse via les instances de ses classes de toute façon.
écrire un @classproperty
décorateur
Vous pouvez en fait créer un classproperty
décorateur en seulement quelques lignes de code en sous-classant property
(il est implémenté en C, mais vous pouvez voir l'équivalent Python ici ):
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))
Traitez ensuite le décorateur comme s'il s'agissait d'une méthode de classe combinée à une propriété:
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
Et ce code devrait fonctionner sans erreur:
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()
Mais je ne sais pas à quel point cela serait bien avisé. Un ancien article de liste de diffusion suggère que cela ne devrait pas fonctionner.
Faire fonctionner la propriété sur la classe:
L'inconvénient de ce qui précède est que la "propriété de classe" n'est pas accessible depuis la classe, car elle écraserait simplement le descripteur de données de la classe __dict__
.
Cependant, nous pouvons remplacer cela avec une propriété définie dans la métaclasse __dict__
. Par exemple:
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
Et puis une instance de classe de la métaclasse pourrait avoir une propriété qui accède à la propriété de la classe en utilisant le principe déjà démontré dans les sections précédentes:
class FooClassProperty(metaclass=MetaWithFooClassProperty):
@property
def foo(self):
"""access the class's property"""
return type(self).foo
Et maintenant nous voyons à la fois l'instance
>>> FooClassProperty().foo
<class '__main__.FooClassProperty'>
et la classe
>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>
avoir accès à la propriété de classe.