次のコードを見てください。
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__
で、正しく機能させることができますが、これが適切な解決策かどうかはわかりません。
答えはこの記事のどこかにあるように感じますが、まだ見つけることができていません。おそらく、メタクラスの使用に頼る必要がありますか?
編集:私のアプリケーションでは、__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