J'ai un cours comme:
class MyClass:
Foo = 1
Bar = 2
Chaque fois que MyClass.Foo
ou MyClass.Bar
est appelé, j'ai besoin d'une méthode personnalisée pour être appelé avant que la valeur ne soit renvoyée. Est-ce possible en Python? Je sais que c'est possible si je crée une instance de la classe et que je peux définir ma propre méthode __getattr__
. Mais mon scénario implique l'utilisation de cette classe en tant que telle sans en créer aucune instance.
De plus, j'ai besoin d'une méthode personnalisée __str__
pour être appelée lorsque str(MyClass.Foo)
est appelé. Python fournit-il une telle option?
__getattr__()
et __str__()
pour un objet se trouvent dans sa classe. Si vous souhaitez personnaliser ces éléments pour une classe, vous avez besoin de la classe de classe. Une métaclasse.
class FooType(type):
def _foo_func(cls):
return 'foo!'
def _bar_func(cls):
return 'bar!'
def __getattr__(cls, key):
if key == 'Foo':
return cls._foo_func()
Elif key == 'Bar':
return cls._bar_func()
raise AttributeError(key)
def __str__(cls):
return 'custom str for %s' % (cls.__name__,)
class MyClass:
__metaclass__ = FooType
# in python 3:
# class MyClass(metaclass=FooType):
# pass
print MyClass.Foo
print MyClass.Bar
print str(MyClass)
impression:
foo!
bar!
custom str for MyClass
Et non, un objet ne peut pas intercepter une demande de codification de l'un de ses attributs. L'objet renvoyé pour l'attribut doit définir son propre comportement __str__()
.
(Je sais que c'est une vieille question, mais puisque toutes les autres réponses utilisent une métaclasse ...)
Vous pouvez utiliser le simple descripteur classproperty
suivant:
class classproperty(object):
""" @classmethod+@property """
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()
Utilisez-le comme:
class MyClass(object):
@classproperty
def Foo(cls):
do_something()
return 1
@classproperty
def Bar(cls):
do_something_else()
return 2
Pour le premier, vous devrez créer une métaclasse et y définir __getattr__()
.
class MyMetaclass(type):
def __getattr__(self, name):
return '%s result' % name
class MyClass(object):
__metaclass__ = MyMetaclass
print MyClass.Foo
Pour le second, non. L'appel de str(MyClass.Foo)
appelle MyClass.Foo.__str__()
. Vous devez donc renvoyer un type approprié pour MyClass.Foo
.
Surpris, personne ne signala celui-ci:
class FooType(type):
@property
def Foo(cls):
return "foo!"
@property
def Bar(cls):
return "bar!"
class MyClass(metaclass=FooType):
pass
Travaux:
>>> MyClass.Foo
'foo!'
>>> MyClass.Bar
'bar!'
(pour Python 2.x, remplacez la définition de MyClass
par:
class MyClass(object):
__metaclass__ = FooType
)
Ce que les autres réponses disent à propos de str
s'applique à cette solution: Elle doit être implémentée sur le type retourné.
En fonction du cas, j'utilise ce modèle
class _TheRealClass:
def __getattr__(self, attr):
pass
LooksLikeAClass = _TheRealClass()
Ensuite, vous importez et utilisez-le.
from foo import LooksLikeAClass
LooksLikeAClass.some_attribute
Ceci évite l'utilisation de métaclasse et gère certains cas d'utilisation.