12

デコレータがどのように機能するかを理解しようとしていますが、デコレートされた関数がデコレータの変数にアクセスできるかどうか疑問に思っていました。たとえば、次のコードで、f1がlocalVariableにアクセスできるようにするにはどうすればよいですか?それは可能ですか、そしてそれは物事を行うための良い方法でさえありますか?

def funcDec(func):
    localVariable = "I'm a local string"
    def func2Return(*args):                                                                            
        print "Calling localVariable from decorator " + localVariable
        func(*args)                                      
        print "done with calling f1"
    return func2Return

@funcDec
def f1(x, y):
    print x + y
    print localVariable

f1(2, 3)
4

3 に答える 3

16

Pythonのスコープ規則により、デコレートされた関数は通常、デコレータの変数にアクセスできません。ただし、関数には任意の属性を割り当てることができるため、デコレータで次のようなことを実行して、同様の効果を得ることができます(同じスコープルールのため)

def funcDec(func):
    localVariable = "I'm a local string"

    def wrapped(*args):
        print("Calling localVariable from funcDec " + localVariable)
        func(*args)
        print("done with calling f1")

    wrapped.attrib = localVariable
    return wrapped

@funcDec
def f1(x, y):
    print(x + y)
    print('f1.attrib: {!r}'.format(f1.attrib))

f1(2, 3)

これにより、次の出力が生成されます。

Calling localVariable from funcDec I'm a local string
5
f1.attrib: "I'm a local string"
done with calling f1

誰かがこれをクラスのメソッドに適用できるかどうか尋ねました。答えは「はい」ですが、クラス自体または引数として渡されたインスタンスのいずれかを介してメソッドを参照する必要があります。self両方の手法を以下に示します。selfコードが含まれているクラスの名前に依存しないため、使用することをお勧めします。

class Test(object):
    @funcDec
    def f1(self):
        print('{}.f1() called'.format(self.__class__.__name__))
        print('self.f1.attrib: {!r}'.format(self.f1.attrib))  # Preferred.
        print('Test.f1.attrib: {!r}'.format(Test.f1.attrib))  # Also works.

print()
test = Test()
test.f1()

出力:

Calling localVariable from funcDec I'm a local string
Test.f1() called
self.f1.attrib: "I'm a local string"
Test.f1.attrib: "I'm a local string"
done with calling f1
于 2012-09-15T10:16:37.170 に答える
8

いいえ、できません。この前の質問を参照してください。関数がデコレータであるからといって、それが呼び出す関数がその変数に特別にアクセスできるという意味ではありません。これを行う場合:

def func():
    a = 2
    otherFunc()

その場合、otherFuncは変数にアクセスできませんa。これがすべての関数呼び出しで機能する方法であり、デコレータでも機能する方法です。

これで、デコレータ内で定義したラッパー関数(このfunc2Return例では)変数にアクセスできます。これは、その関数が字句的にこれらの変数と同じスコープにあるためです。だからあなたのラインprint "Calling localVariable from decorator " + localVariableは機能します。これをある程度使用して、デコレータの変数に依存する動作でデコレートされた関数をラップできます。しかし、実際に装飾されている関数(このf1例では)は、これらの変数にアクセスできません。

関数は、関数定義が実際にあるスコープからのローカル変数にのみアクセスできます。関数は、スコープの呼び出しから変数を取得しません。(これは良いことです。もしそうなら、それは大きな混乱になるでしょう。)

于 2012-09-15T08:33:03.753 に答える
8

デコレータを覚えておけば助かると思います

@deco
def f(...): ...

のための単なる構文糖です

def f(...): ...
f = deco(f)

ある種のマクロ展開ではなく。Pythonでは、変数のスコープは静的に決定されるため、グローバル(モジュールレベル)関数の場合、引数として渡されたり割り当てられたりしない変数は、グローバル名前空間で検索されます。

したがって、func2Return()のローカル変数を明示的に渡す必要があります。の署名を変更しf1f1(x, y, localvariable=None)ラッパー関数fun2Returnでそれを呼び出すようにします

f1(*args, localvariable=localvariable)
于 2012-09-15T08:42:24.150 に答える