Посмотрите на следующий код:Как получить доступ к атрибутам класса суперкласса в Python?
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__
, я могу заставить его работать исправно, но я не уверен, что это правильное решение.
У меня такое чувство, что ответ находится где-то в this article, но я еще не смог его найти. Возможно, мне нужно прибегнуть к использованию метакласса?
Редактировать: В моей заявке __getattr__
первые чеки self.base
. Если это не None
, атрибут должен быть получен оттуда. Только в другом случае необходимо вернуть значение по умолчанию. Возможно, я мог бы переопределить __getattribute__
. Будет ли это лучшим решением?
Редактировать 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
Как насчет размещения значений по умолчанию непосредственно в пространстве имен классов вместо использования словаря 'defaults'? Это сделало бы любую магию ненужной - она просто сработала бы. –
@Sven Я знал, что должен был упомянуть об этом :) В моем приложении '__getattr__' сначала проверяет другой атрибут (база). Только если другой атрибут None, значение по умолчанию должно быть возвращено. В противном случае атрибут нужно искать в базе. –
Да, вот как атрибуты экземпляра/класса работают в Python. :) Проверьте ответ Тони. Afaik его код будет делать точно так же, как ваш. –