0

通常の状況では、ユーザー入力によって提供される引数で呼び出される関数がいくつかあります。ただし、一部のシステム状態に基づいて実行時に決定される特定の一連の引数を使用して、これらの関数の一部を呼び出すことは有効です。

ユーザーがオプションで、すべての有効な入力でこれらの関数を呼び出し、各呼び出しの結果を返すようにプログラムに指示できるようにしたいと考えています。使用する一連の引数を示す別のデコレータを持つ関数のアクティベーションスイッチのように機能するデコレータがうまく機能すると思います。

さらに、関数のシグネチャとメタデータを保持する必要があります。それは私のプログラムの操作に不可欠です。

これは私が試したものですが、うまくいきません。この例に基づいています。

>>> from decorator import decorator
>>> def the_list():
...     return ["foo", "bar", "baz"]
... 
>>> import itertools
>>> @decorator
... def do_all(func):
...     # this will do nothing (besides wrap in a tuple) unless func is decorated with @gets_arg_from
...     if hasattr(func, 'get_from'):
...         return tuple(func(*args) for args in itertools.product(*(list_fun() for list_fun in func.get_from)))
...     else:
...         return (func(),)
... 
>>> def gets_arg_from(*list_funcs):
...     # this will do nothing to func unless decorated with @do_all
...     def gets_arg_from(func, *args, **kwargs):
...         func.get_from = list_funcs
...         return func(*args, **kwargs)
...     return decorator(gets_arg_from)
... 
>>> @gets_arg_from(the_list)
... def print_func(word):
...     # this will print its argument unless decorated with @do_all
...     # at that point it will print every element returned by the_list()
...     print word
... 
>>> print_func("foo")
foo
>>> all = decorator(do_all, print_func)
>>> all()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print_func() takes exactly 1 argument (0 given)
>>> print_func.get_from
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'get_from'

私が期待したのは:

>>> all()
("foo", "bar", "baz")

私が気づいたことは間違っています:

  1. gets_arg_fromget_fromに属性を追加しませんfunc
  2. 表記法を使用している私の何か@gets_arg_from(the_list)が間違っています。2 つの引数を渡そうとしていると思われます (しかし、なぜそれが問題になるのでしょうか?)

私の動機としては、これらのルーチンが文字通り何百もあり、それらの実装の詳細 (および機能の正確さ) が頻繁に変更される可能性があるため、デコレータについて考えてinspectいます。引数名に基づいて、do_all意味のある各関数の機能をハードコーディングしたくありません。クラス メソッドは機能するかもしれませんが、私の目的では、それらは意味論的に工夫されています。さらに、私のコードを維持しなければならない可能性のある他の人のために、特定の引数名を使用したり、特定のクラスに関数を配置したりするよりも、デコレーターを適用して残りを気にしないように依頼する方が簡単だと思います。なんでもいい。この質問は奇妙に聞こえるかもしれませんが、この脚注が私を狂人のように見せるのに役立つかもしれないと考えました.

4

1 に答える 1

1

これはあなたが望んでいることではありませんか?

import functools
from itertools import product

def the_list():
    return ["foo", "bar", "baz"]


def do_all(func):
    if hasattr(func, 'get_from'):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            return tuple(func(*args) for args in
                         product(*(lf() for lf in func.get_from)))
        return wrapper
    return func

def gets_arg_from(*list_funcs):
    def decorator(func):
        func.get_from = list_funcs
        return func
    return decorator

@gets_arg_from(the_list)
def print_func(word):
    return word

print print_func('foo')

all = do_all(print_func)
print all()

編集:説明

これら 2 つのコード セグメントは同一です。

@deco
def func(...):
    some code

と同じです

func = deco(lambda ...: some code)

@something は、関数呼び出しと無名関数の作成のための単なる構文糖衣です...

次の平和なコードで何が起こったのかを順を追って説明します。

@gets_arg_from(the_list)
def print_func(word):
     return word
  1. word最初の Python は、パラメーターを受け取り、これを返すだけの本体を持つ無名関数を作成しますword(または、関数本体が行うことは何でも行います)。

  2. 次に、関数get_arg_fromが呼び出さthe_listれ、引数として渡されます

  3. get_arg_fromdecorator関数を作成して返す

  4. decoratorから返された関数がget_arg_from呼び出され (これは構文糖衣です) func、ステップ 1 で作成された無名関数を引数として渡します。

  5. decoratorlist_funcsタプルを無名関数の属性に割り当て、get_from無名関数を返すだけです

  6. 関数の戻り値はdecorator変数に代入されますprint_func

同様の効果は、次の方法で実現できます。

def __anonimous(word):
    return word
__decorator = gets_arg_from(the_list)
print_func = __decorator(__anonimous)

したがって、基本的にgets_arg_fromはデコレータではなく、デコレータを返す関数です。

do_all一方、デコレータ関数を引数として受け取り、元の関数 (関数に属性がない場合get_from) またはwrapper元の関数を置き換える関数 (get_from属性がある場合) を返します。

ここでさらに例を見つけることができます。

于 2013-08-02T19:09:12.993 に答える