web-dev-qa-db-fra.com

__getattr__ pour les variables statiques/de classe en python

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?

49
Tuxdude

__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__().

60
Matt Anderson

(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
14
shx2

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.

9

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é.

5
Jonas Schäfer

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.

0
geckos