__getattr__と__getattribute__の違いを理解する
-
28-09-2019 - |
質問
私は間の違いを理解しようとしています __getattr__
と __getattribute__
, しかし、私はそれに失敗しています。
答え スタックオーバーフローの質問に との差 __getattr__
vs __getattribute__
言う:
__getattribute__
オブジェクト上の実際の属性を調べる前に呼び出されるため、正しく実装するのが難しい場合があります。あなたは無限の再帰に非常に簡単に終わることができます。
それが何を意味するのか全くわかりません。
それからそれは次のように言い続けます:
あなたはほぼ間違いなく欲しいです
__getattr__
.
なんで?
私はそれを読みました __getattribute__
失敗する、 __getattr__
呼ばれています。では、なぜ同じことをする2つの異なる方法があるのですか?私のコードが新しいスタイルのクラスを実装している場合、私は何を使用すればよいですか?
この質問をクリアするためのコード例をいくつか探しています。私は自分の能力を最大限に活用しましたが、私が見つけた答えは問題について徹底的に議論していません。
ドキュメントがある場合は、それを読む準備ができています。
解決
最初にいくつかの基本。
オブジェクトを使用すると、その属性を処理する必要があります。通常、私たちはします instance.attribute
. 。より多くの制御が必要な場合があります(事前に属性の名前がわからない場合)。
例えば、 instance.attribute
なります getattr(instance, attribute_name)
. 。このモデルを使用して、 属性名 文字列として。
の使用 __getattr__
また、クラスに明示的に管理していない属性を処理する方法を伝えることもできます。 __getattr__
方法。
Pythonは、まだ定義されていない属性を要求するたびにこのメソッドを呼び出しますので、それをどうするかを定義できます。
古典的なユースケース:
class A(dict):
def __getattr__(self, name):
return self[name]
a = A()
# Now a.somekey will give a['somekey']
警告と使用 __getattribute__
すべての属性をキャッチする必要がある場合 それが存在するかどうかに関係なく, 、 使用する __getattribute__
代わりは。違いはそれです __getattr__
実際には存在しない属性に対してのみ呼び出されます。属性を直接設定した場合、その属性を参照してください。 __getattr__
.
__getattribute__
常に呼ばれています。
他のヒント
__getattribute__
属性アクセスが発生するたびに呼び出されます。
class Foo(object):
def __init__(self, a):
self.a = 1
def __getattribute__(self, attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
f = Foo(1)
f.a
これにより、無限の再帰が発生します。ここの犯人は線です return self.__dict__[attr]
. 。すべての属性が保存されているふりをしましょう(真実に十分近い) self.__dict__
そして彼らの名前で利用可能です。この線
f.a
アクセスしようとします a
の属性 f
. 。これは呼び出します f.__getattribute__('a')
. __getattribute__
その後、ロードしようとします self.__dict__
. __dict__
の属性です self == f
そして、Pythonコール f.__getattribute__('__dict__')
これも属性にアクセスしようとします '__dict__
'。これは無限の再帰です。
もしも __getattr__
代わりに使用されていました
- それは決して実行されなかったでしょう
f
持っていますa
属性。 - それが実行された場合、(あなたが求めたと言ってみましょう
f.b
)その後、見つけるために呼ばれていなかったでしょう__dict__
すでにそこにあるからです__getattr__
ifの場合にのみ呼び出されます 属性を見つける他のすべての方法は失敗しました.
上記のクラスを使用して書くための「正しい」方法 __getattribute__
は
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)
バインドします __getattribute__
「最も近い」スーパークラスの方法(正式には、クラスのメソッド解像度順序の次のクラス、またはMRO) self
そして、それを呼び出して、それが仕事をするようにします。
この問題はすべて、使用することで回避されます __getattr__
Pythonを使用できます それは普通のことです 属性が見つからないまで。その時点で、Pythonハンドはあなたに制御します __getattr__
方法とそれが何かを思い付くことができます。
また、あなたが無限の再帰に出くわすことができることに注目する価値があります __getattr__
.
class Foo(object):
def __getattr__(self, attr):
return self.attr
私はそれを練習として残します。
他の答えは、間の違いを説明する素晴らしい仕事をしたと思います __getattr__
と __getattribute__
, 、しかし、明確ではないかもしれないことの1つは、あなたが使用したい理由です __getattribute__
. 。クールなこと __getattribute__
クラスにアクセスするときに、本質的にドットにオーバーロードできるということです。これにより、低レベルで属性にアクセスする方法をカスタマイズできます。たとえば、自己引数のみをとるすべての方法が特性として扱われるクラスを定義したいとします。
# prop.py
import inspect
class PropClass(object):
def __getattribute__(self, attr):
val = super(PropClass, self).__getattribute__(attr)
if callable(val):
argcount = len(inspect.getargspec(val).args)
# Account for self
if argcount == 1:
return val()
else:
return val
else:
return val
そして、インタラクティブな通訳から:
>>> import prop
>>> class A(prop.PropClass):
... def f(self):
... return 1
...
>>> a = A()
>>> a.f
1
もちろん、これはばかげた例であり、おそらくこれをやりたくないでしょうが、それはあなたがオーバーライドから得ることができる力をあなたに示しています __getattribute__
.