Посмотрите на следующий код:
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) # infinite recursion
#return cls.__mro__[1].get_default(name) # this works, though
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c = C()
print('c.a =', c.a)
У меня есть иерархия классов, каждый со своим собственным словарем, содержащим некоторые значения по умолчанию.Если экземпляр класса не имеет определенного атрибута, вместо него должно быть возвращено значение по умолчанию.Если значение по умолчанию для атрибута не содержится в словаре defaults
текущего класса, следует искать в словаре defaults
суперкласса.
Я пытаюсь реализовать это с помощью метода рекурсивного класса get_default
.К сожалению, программа застревает в бесконечной рекурсии.Моего понимания super()
явно не хватает.Получив доступ к __mro__
, я могу заставить его работать должным образом, но я не уверен, что это правильное решение.
У меня такое чувство, что ответ где-то в этой статье , но я пока не смог его найти.Возможно, мне нужно прибегнуть к использованию метакласса?
edit: В моем приложении __getattr__
сначала проверяет self.base
.Если это не None
, атрибут должен быть извлечен оттуда.Только в другом случае должно быть возвращено значение по умолчанию.Я мог бы переопределить __getattribute__
.Будет ли это лучшим решением?
edit 2: Ниже приведен расширенный пример функциональности, которую я ищу.В настоящее время он реализован с использованием __mro__
(более раннее предложение unutbu, в отличие от моего исходного рекурсивного метода).Если кто-то не может предложить более элегантное решение, я счастлив использовать эту реализацию.Я надеюсь, что это проясняет ситуацию.
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
Вывод:
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