2011-11-29 7 views
4

私はいくつかの人々にデコレータの有用性を実証したかったのですが、単純な例では失敗しました。引数を持たない2つの関数fgを考えてみましょう。 f()+ g()を返す関数として、その和f + gを定義することができます。もちろん、関数の追加、減算などは一般的に定義されていません。しかし、すべての関数を追加可能な関数に変換するデコレータを書くのは簡単です。なぜこの "実行可能な関数"のPython実装が失敗しますか?

ここでは、任意の関数を "操作可能な"関数、つまり標準モジュールoperatorの任意の演算子について前述の方法で動作する関数に変換するデコレータを使用したいと考えています。次のように私の実装が見えます:

import operator 

class function(object): 
    def __init__(self, f): 
     self.f = f 
    def __call__(self): 
     return self.f() 

def op_to_function_op(op): 
    def function_op(self, operand): 
     def f(): 
      return op(self(), operand()) 
     return function(f) 
    return function_op 
binary_op_names = ['__add__', '__and__', '__div__', '__eq__', '__floordiv__', '__ge__', '__gt__', '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__or__', '__pow__', '__sub__', '__truediv__', '__xor__'] 
for name in binary_op_names: 
    type.__setattr__(function, name, op_to_function_op(getattr(operator, name))) 

はのは、それが動作するかどうかを確認するために少しテストを実行してみましょう:

@function 
def a(): 
    return 4 

def b(): 
    return 7 

c = a + b 
print c() 
print c() == operator.__add__(4, 7) 

は出力:

11 
True 

これは、私はいくつかの実験の後に得た最終版であります。 は、今度は私が試したどのような顔をしているために、2つの小、無関係な修正をやらせる前に:

まずbinary_op_namesの定義では、丸括弧に角括弧を変更します。突然、完全に無関係のエラーメッセージが出てきます。

Traceback (most recent call last): 
    File "example.py", line 30, in <module> 
    c = a + b 
TypeError: unsupported operand type(s) for +: 'function' and 'function' 

これはどこから来ましたか?

第二:これは私には見えます

3 
False 
True 

:出力

@function 
def a(): 
    return 4 

def b(): 
    return 7 

c = a + b 
print c() 
print c() == operator.__add__(4, 7) 
print c() == operator.__xor__(4, 7) 

:ややより複雑なテストケースを実行し

op = getattr(operator, name) 
type.__setattr__(function, name, lambda self, other: function(lambda: op(self(), other()))) 

:ラムダ式としてop_to_function_opを書きますスコープのような漏れが、再び私はドンなぜこれが起こるのか理解していない。

+0

タイプ.__ setattr__は悪何かをするようですが...ドン何を知っているの?タプルするためのリストは本当に奇妙です。タイプを変更した場合。setattrへの__setattr__はすべて動作するようです。 – mkorpela

+0

典型的な 'memoize'デコレータはフィボナッチシーケンスに適用され、他のキャッシュに優しい問題はデコレータのデモンストレーションのためのより良いアプリケーションかもしれません。 – Daenyth

+0

タプルの問題は厄介なバグのようです。私は同じ行動を見ている。 'name'に関するすべてのことは絶対にチェックアウトしますが、' type .__ setattr__'は動作しません。 – Nate

答えて

2

最初の問題については、からtupleに変更するときに何も問題は見られませんでしたが、なぜそれを確認したのかはわかりません。

2番目の問題については、に設定された最後のアイテムが__xor__であったため、すべての操作でXORが実行されます。 opは作成時にラムダに渡されないため、ラムダが呼び出されるたびにグローバルスコープ内でopが検索され、常に__xor__が表示されます。あなたの現在のop_tofunction_opと同様に、クロージャとして機能ラムダを作成することによって、これを防ぐことができます

、それはこのようなものを見に終わるかもしれない:

op_to_function_op = lambda f: lambda self, other: function(lambda: f(self(), other())) 
for name in binary_op_names: 
    op = getattr(operator, name) 
    type.__setattr__(function, name, op_to_function_op(op)) 

>>> (function(lambda: 4) + (lambda: 7))() 
11 
>>> (function(lambda: 4) - (lambda: 7))() 
-3 
>>> (function(lambda: 4)^(lambda: 7))() 
3 
+0

タイプ.__ setattr__またはsetattrを使用しましたか? (メインのコメントスレッドの説明を参照してください) lambdasの説明をありがとう! – Turion

+0

私は 'type .__ setattr__'を使っていましたが、うまくいったようですが、' setattr'や 'type .__ setattr__'はあまり使われていません。 –

関連する問題