__getattr__モジュール
-
20-09-2019 - |
質問
どのように実施する者と同等以上の __getattr__
aクラス、モジュール?
例
を行う関数を呼び出すときには存在しないモジュールの静的に定義属性で、私の作成クラスのインスタンスによるモジュールの呼び出しの方法で同じ名前のとして失敗した属性のルックアップのモジュールです。
class A(object):
def salutation(self, accusative):
print "hello", accusative
# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
return getattr(A(), name)
if __name__ == "__main__":
# i hope here to have my __getattr__ function above invoked, since
# salutation does not exist in the current namespace
salutation("world")
る:
matt@stanley:~/Desktop$ python getattrmod.py
Traceback (most recent call last):
File "getattrmod.py", line 9, in <module>
salutation("world")
NameError: name 'salutation' is not defined
解決
しばらく前からGuido宣言されたすべての特殊な方法でルックアップに
新しい形式のクラスではバイパス __getattr__
や __getattribute__
.Dunder方法なったモジュールだが、例えば、モジュールのコンテクストとして長単に定義 __enter__
や __exit__
, 前者の技 破.
最近では、歴史的特親、モジュール __getattr__
その中で、既存のhack(モジュールに置き換え自体のクラスに sys.modules
インポート時には必要不要になりました。
Python3.7+、あなただけのものを使用は明らかです。カスタマイズの属性にアクセスがモジュール定義の設定 __getattr__
機能のモジュールレベルを受けとる一つの引数(name属性)を返し計算した値を AttributeError
:
# my_module.py
def __getattr__(name: str) -> Any:
...
この設備はフックの"から"輸入、すなわちに戻すことができ、動的に生成物のための諸表など from my_module import whatever
.
関連のモジュールgetattrもできますを定義する __dir__
機能モジュールレベルに対応 dir(my_module)
.見 PEP562 ます。
他のヒント
ここで遭遇する基本的な問題が 2 つあります。
__xxx__
メソッドはクラスのみを検索しますTypeError: can't set attributes of built-in/extension type 'module'
(1) は、どのソリューションでもどのモジュールが検査されているかも追跡する必要があることを意味します。 毎 この場合、モジュールはインスタンス置換動作を行うことになります。そして、(2) ということは、(1) も不可能だということです...少なくとも直接的には。
幸いなことに、sys.modules はそこに何を入れるかについてはこだわりがないので、ラッパーは機能しますが、モジュールへのアクセスに対してのみ機能します (つまり、 import somemodule; somemodule.salutation('world')
;同じモジュールにアクセスするには、置換クラスからメソッドをヤンクして、それらを追加する必要があります。 globals()
クラスのカスタムメソッドを使用します(私は使用するのが好きです) .export()
)または一般的な関数(すでに回答としてリストされているものなど)を使用します。留意すべき点が 1 つあります。ラッパーが毎回新しいインスタンスを作成し、グローバル ソリューションがそうでない場合、動作は微妙に異なります。ああ、両方を同時に使用することはできません。どちらか一方を使用する必要があります。
アップデート
から グイド・ファン・ロッサム:
実際に時々使用され、推奨されるハックがあります。モジュールは、目的の機能を持つクラスを定義でき、最後にsys.モジュールに置き換えます。そのクラスのインスタンス(または、主張する場合はクラスになりますが、一般的に有用ではありません)。例えば。:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
これは、輸入機械がこのハッキングを積極的に可能にし、その最終ステップが実際のモジュールをsys.モジュールから引き出したため、ロードした後に機能します。(これは偶然ではありません。ハックはずっと前に提案されており、輸入機械でそれをサポートするのに十分な好みを決めました。)
したがって、あなたが望むことを達成する確立された方法は、モジュール内に単一のクラスを作成し、モジュールの最後の動作として置換することです。 sys.modules[__name__]
クラスのインスタンスを使って -- これで遊べるようになります __getattr__
/__setattr__
/__getattribute__
必要に応じて。
この機能を使用すると、グローバル、他の関数など、モジュール内の他のすべてのものが失われることに注意してください。 sys.modules
割り当てが行われるため、必要なものがすべて置換クラス内にあることを確認してください。
これはハックですが、クラスでモジュールをラップすることができます:
class Wrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
# Perform custom logic here
try:
return getattr(self.wrapped, name)
except AttributeError:
return 'default' # Some sensible default
sys.modules[__name__] = Wrapper(sys.modules[__name__])
私たちは、通常はそのようにいたしません。
私たちがやっていることはこれです。
class A(object):
....
# The implicit global instance
a= A()
def salutation( *arg, **kw ):
a.salutation( *arg, **kw )
なぜ?暗黙のグローバルインスタンスが表示されるようにます。
例については、少しあなたは「シンプル」乱数ジェネレータをしたいユースケースを簡素化するために、暗黙のグローバルインスタンスを作成しますrandom
モジュール、見てます。
ようお@Håvardの提案したり、場を実施する上で必要となる一部のマジックモジュールなど __getattr__
になるようならを定義する新しいクラスを継承 types.ModuleType
に入れる sys.modules
(おそらく置き換えるモジュールもカスタム ModuleType
定義した).
の主要な __init__.py
ファイルの Werkzeug かなりの実施。
これはハックですが、...
import types
class A(object):
def salutation(self, accusative):
print "hello", accusative
def farewell(self, greeting, accusative):
print greeting, accusative
def AddGlobalAttribute(classname, methodname):
print "Adding " + classname + "." + methodname + "()"
def genericFunction(*args):
return globals()[classname]().__getattribute__(methodname)(*args)
globals()[methodname] = genericFunction
# set up the global namespace
x = 0 # X and Y are here to add them implicitly to globals, so
y = 0 # globals does not change as we iterate over it.
toAdd = []
def isCallableMethod(classname, methodname):
someclass = globals()[classname]()
something = someclass.__getattribute__(methodname)
return callable(something)
for x in globals():
print "Looking at", x
if isinstance(globals()[x], (types.ClassType, type)):
print "Found Class:", x
for y in dir(globals()[x]):
if y.find("__") == -1: # hack to ignore default methods
if isCallableMethod(x,y):
if y not in globals(): # don't override existing global names
toAdd.append((x,y))
for x in toAdd:
AddGlobalAttribute(*x)
if __name__ == "__main__":
salutation("world")
farewell("goodbye", "world")
これは、グローバル・ネームスペース内のすべてのオブジェクトを反復処理することによって動作します。項目がクラスである場合、それはクラス属性を反復処理しました。属性は呼び出し可能である場合には、機能としてグローバル名前空間に追加します。
これは "__" が含まれるすべての属性を無視します。
私は、生産コードでこれを使用することはありませんが、それはあなたが始める必要があります。
ここに私自身の謙虚な貢献だ - HåvardSの高格付けの答え@のわずかな装飾は、しかし、もう少し明確な(それはOPのための十分なS.Lott @、たとえおそらくよくないに受け入れられるかもしれません)。
import sys
class A(object):
def salutation(self, accusative):
print "hello", accusative
class Wrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
try:
return getattr(self.wrapped, name)
except AttributeError:
return getattr(A(), name)
_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])
if __name__ == "__main__":
_globals.salutation("world")
あなたのクラスがあり、あなたのモジュールファイルを作成します。モジュールをインポートします。インポートしたモジュールにgetattr
を実行します。あなたは__import__
を使用して動的インポートを行うと、sys.modulesからモジュールを引くことができます。
ここでは、あなたのモジュールsome_module.py
です:
class Foo(object):
pass
class Bar(object):
pass
また、別のモジュールでます:
import some_module
Foo = getattr(some_module, 'Foo')
動的これをやってます:
import sys
__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')
いくつかの状況ではglobals()
辞書は、例えば、あなたがグローバルスコープから名前でクラスをインスタンス化することができ、十分で
from somemodule import * # imports SomeClass
someclass_instance = globals()['SomeClass']()