2012-09-15 6 views
6

デコレータがどのように機能しているかを理解しようとしていて、装飾機能がデコレータの変数にアクセスできるかどうか疑問に思っていました。たとえば、次のコードでは、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

いいえ、できません。 this previous questionを参照してください。関数がデコレータであるという理由だけで、それが呼び出す関数がその変数に特別なアクセスを持つことを意味するわけではありません。あなたがこれを行う場合:

def func(): 
    a = 2 
    otherFunc() 

はその後otherFuncは変数aにアクセスすることはできません。それがすべての関数呼び出しでどのように機能するのか、それはデコレータでも同じように機能します。その関数は、これらの変数と同じスコープ内にリストに定義されているため

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

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

+0

も参照してください:http://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_and_dynamic_scoping – georg

5

私はあなたが心に留めておくならば、それはデコレータ

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

のためだけの構文糖であることができますだと思います何らかの種類のマクロ展開ではなく、
def f(...): ... 
f = deco(f) 

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

したがって、func2Return()のローカル変数を明示的に渡す必要があります。装飾された機能は、一般的にデコレータ内の任意の変数にアクセスすることはできません、f1(x, y, localvariable=None)f1の署名を変更しているので、Pythonのスコープ規則の

f1(*args, localvariable=localvariable) 
7

fun2Returnはそれを呼び出すラッパー関数を持っています。

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 decorator 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 
+0

素晴らしい例を!クラス内のメソッドでも同じことができますか? –

+0

@KeerthanaPrabhakaran:はい、デコレータはクラスメソッドで動作しますが、追加された属性を異なる方法で参照する必要があります。つまり、 'self.method_name.attrib'のようなものです。 – martineau

+0

ええ、私はそれを試みました!しかし、 'instancemethod'オブジェクトには属性 'attrib'がありません。 –

関連する問題