web-dev-qa-db-fra.com

Comment remplacer __getattr__ dans Python sans rompre le comportement par défaut?

Je veux remplacer le __getattr__ méthode sur une classe pour faire quelque chose de chic mais je ne veux pas casser le comportement par défaut.

Quelle est la bonne façon de faire cela?

171
sheats

Remplacement __getattr__ devrait être bon - __getattr__ n'est appelé qu'en dernier recours, c'est-à-dire s'il n'y a pas d'attribut dans l'instance correspondant au nom. Par exemple, si vous accédez à foo.bar, puis __getattr__ ne sera appelé que si foo ne possède aucun attribut appelé bar. Si l'attribut est un attribut que vous ne souhaitez pas gérer, augmentez AttributeError:

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

Cependant, contrairement à __getattr__, __getattribute__ sera appelé en premier (ne fonctionne que pour les nouvelles classes de style, c’est-à-dire celles qui héritent d’un objet). Dans ce cas, vous pouvez conserver le comportement par défaut comme suit:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

Voir the Python docs pour plus) .

254
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False
32
Roger Pate

Pour étendre la réponse à Michael, si vous souhaitez conserver le comportement par défaut à l'aide de __getattr__, tu peux le faire comme ça:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

Maintenant, le message d'exception est plus descriptif:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'
9
José Luis