Jetez un œil au code suivant :
class A(object):
defaults = {'a': 1}
def __getattr__(self, name):
print('A.__getattr__')
return self.get_default(name)
@classmethod
def get_default(cls, name):
# some debug output
print('A.get_default({}) - {}'.format(name, cls))
try:
print(super(cls, cls).defaults) # as expected
except AttributeError: #except for the base object class, of course
pass
# the actual function body
try:
return cls.defaults[name]
except KeyError:
return super(cls, cls).get_default(name) # récursion infinie
#return cls.__mro__[1].get_default(name) # cela fonctionne, cependant
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c = C()
print('c.a =', c.a)
J'ai une hiérarchie de classes, chacune avec son propre dictionnaire contenant des valeurs par défaut. Si une instance d'une classe n'a pas un attribut particulier, une valeur par défaut pour celui-ci devrait être renvoyée à la place. S'il n'y a pas de valeur par défaut pour l'attribut dans le dictionnaire defaults
de la classe actuelle, le dictionnaire defaults
de la superclasse devrait être recherché.
Je tente de mettre en œuvre cela en utilisant la méthode de classe récursive get_default
. Malheureusement, le programme reste coincé dans une récursion infinie. Ma compréhension de super()
est manifestement insuffisante. En accédant à __mro__
, je peux arriver à le faire fonctionner correctement, mais je ne suis pas sûr que ce soit une solution appropriée.
J'ai l'impression que la réponse se trouve quelque part dans cet article, mais je n'ai pas encore réussi à la trouver. Peut-être devrais-je recourir à l'utilisation d'une métaclass ?
edit: Dans mon application, __getattr__
vérifie d'abord self.base
. S'il n'est pas égal à None
, l'attribut doit être récupéré à partir de là. Seulement dans l'autre cas, une valeur par défaut doit être renvoyée. Je pourrais probablement remplacer __getattribute__
. Est-ce que ce serait la meilleure solution ?
edit 2: Ci-dessous se trouve un exemple étendu de la fonctionnalité que je recherche. Elle est actuellement implémentée en utilisant __mro__
(la suggestion précédente de unutbu, par opposition à ma méthode récursive d'origine). À moins que quelqu'un ne puisse suggérer une solution plus élégante, je suis satisfait d'utiliser cette implémentation. J'espère que cela éclaircit les choses.
class A(object):
defaults = {'a': 1}
def __init__(self, name, base=None):
self.name = name
self.base = base
def __repr__(self):
return self.name
def __getattr__(self, name):
print("'{}' attribute not present in '{}'".format(name, self))
if self.base is not None:
print(" getting '{}' from base ({})".format(name, self.base))
return getattr(self.base, name)
else:
print(" base = None; returning default value")
return self.get_default(name)
def get_default(self, name):
for cls in self.__class__.__mro__:
try:
return cls.defaults[name]
except KeyError:
pass
raise KeyError
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c1 = C('c1')
c1.b = 55
print('c1.a = ...'); print(' ...', c1.a) # 1
print(); print('c1.b = ...'); print(' ...', c1.b) # 55
print(); print('c1.c = ...'); print(' ...', c1.c) # 3
c2 = C('c2', base=c1)
c2.c = 99
print(); print('c2.a = ...'); print(' ...', c2.a) # 1
print(); print('c2.b = ...'); print(' ...', c2.b) # 55
print(); print('c2.c = ...'); print(' ...', c2.c) # 99
La sortie :
c1.a = ...
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c1.b = ...
... 55
c1.c = ...
'c' attribute not present in 'c1'
base = None; returning default value
... 3
c2.a = ...
'a' attribute not present in 'c2'
getting 'a' from base (c1)
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c2.b = ...
'b' attribute not present in 'c2'
getting 'b' from base (c1)
... 55
c2.c = ...
... 99