140

__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
4

8 に答える 8

124

ここで発生している 2 つの基本的な問題があります。

  1. __xxx__メソッドはクラスでのみ検索されます
  2. TypeError: can't set attributes of built-in/extension type 'module'

(1) どのソリューションも、どのモジュールが検査されているかを追跡する必要があることを意味します。そうしないと、すべてのモジュールがインスタンス置換動作を行うことになります。(2) は、(1) が不可能であることを意味します...少なくとも直接的には不可能です。

幸いなことに、sys.modules はそこに何が入るかについてうるさいわけではないので、ラッパーは機能しますが、モジュール アクセスに対してのみ機能します (つまりimport somemodule; somemodule.salutation('world')、同じモジュール アクセスの場合、ほとんどの場合、置換クラスからメソッドをヤンクし、それらを別のクラスに追加する必要globals()があります)。クラスのカスタムメソッド(私は を使用するのが好きです.export())またはジェネリック関数(すでに回答としてリストされているものなど)を使用します覚えておくべきこと:ラッパーが毎回新しいインスタンスを作成していて、グローバルソリューションがそうでない場合ああ、同時に両方を使用することはできません-どちらか一方です.


アップデート

グイド・ヴァン・ロッサム より:

実際には、時々使用され、推奨されるハックがあります。モジュールは、必要な機能を持つクラスを定義し、最後に、sys.modules 内の自分自身をそのクラスのインスタンス (または、主張する場合はクラス) に置き換えることができます。 、しかしそれは一般的にあまり役に立ちません)。例えば:

# module foo.py

import sys

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>

sys.modules[__name__] = Foo()

これが機能するのは、インポート機構が積極的にこのハックを有効にしているためです。また、最後のステップとして、実際のモジュールをロード後に sys.modules から引き出します。(これは偶然ではありません。ハックはずっと前に提案されていたので、輸入機械でそれをサポートするのに十分だと判断しました。)

したがって、目的を達成するための確立された方法は、モジュール内に単一のクラスを作成し、モジュールの最後の動作としてクラスのインスタンスに置き換えることです。これで、必要に応じて/ /sys.modules[__name__]で遊ぶことができます。__getattr____setattr____getattribute__


注 1 : この機能を使用すると、モジュール内の他のもの (グローバル、その他の関数など) がsys.modules割り当て時に失われます。そのため、必要なものがすべて置換クラス内にあることを確認してください。

注 2 : サポートするには、クラスで定義from module import *する必要があります。__all__例えば:

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>
    __all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})

Python のバージョンによっては、省略できる名前が他にもある場合があります__all__。Python 2 とのset()互換性が不要な場合は、 を省略できます。

于 2011-10-05T21:59:51.303 に答える
50

これはハックですが、モジュールをクラスでラップできます。

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__])
于 2010-03-15T13:24:22.480 に答える
20

私たちは通常、そのようにはしません。

私たちがしていることはこれです。

class A(object):
....

# The implicit global instance
a= A()

def salutation( *arg, **kw ):
    a.salutation( *arg, **kw )

なんで?暗黙的なグローバル インスタンスが表示されるようにします。

例としてrandom、「単純な」乱数ジェネレーターが必要なユースケースをわずかに単純化するために暗黙的なグローバルインスタンスを作成するモジュールを見てください。

于 2010-03-15T13:25:57.313 に答える
13

@Håvard S が提案したものと同様に、モジュールに魔法を実装する必要がある場合 ( など__getattr__)、継承する新しいクラスを定義してtypes.ModuleTypeそれを入れsys.modulesます (おそらく、カスタムModuleTypeが定義されたモジュールを置き換えます)。

これのかなり堅牢な実装については、 Werkzeugのメイン__init__.pyファイルを参照してください。

于 2010-03-15T15:02:49.987 に答える
9

これはハックですが...

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")

これは、グローバル名前空間内のすべてのオブジェクトを反復処理することによって機能します。アイテムがクラスの場合、クラス属性を反復処理します。属性が呼び出し可能な場合、関数としてグローバル名前空間に追加されます。

「__」を含むすべての属性を無視します。

私はこれを本番コードでは使用しませんが、開始する必要があります。

于 2010-05-05T20:35:45.197 に答える
5

これは私自身の謙虚な貢献です-@ Håvard Sの高評価の回答を少し装飾しますが、もう少し明示的です(おそらく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")
于 2013-07-12T22:33:56.250 に答える
-3

クラスを含むモジュール ファイルを作成します。モジュールをインポートします。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')
于 2010-03-15T15:09:00.473 に答える