3

タグに基づいて関数を有効または無効にするために、関数タグ付けシステムを作成しています。

def do_nothing(*args, **kwargs): pass

class Selector(set):
    def tag(self, tag):
        def decorator(func):
            if tag in self:
                return func
            else:
                return do_nothing
        return decorator

selector = Selector(['a'])

@selector.tag('a')
def foo1():
    print "I am called"

@selector.tag('b')
def foo2():
    print "I am not called"

@selector.tag('a')
@selector.tag('b')
def foo3():
    print "I want to be called, but I won't be"

foo1() #Prints "I am called"
foo2() #Does nothing
foo3() #Does nothing, even though it is tagged with 'a'

私の質問は、最後の関数foo3についてです。なぜ呼ばれていないのか理解しています。セレクターにタグが存在する場合に呼び出されるようにする方法があるかどうか疑問に思いました。理想的には、このソリューションでは、関数が呼び出されるたびにではなく、タグが1回だけチェックされるようになっています。

補足:単体テストの環境変数に基づいて実行するテストを選択するためにこれを行っていますunittest。私の実際の実装ではを使用していunittest.skipます。

編集:デコレータリターンを追加しました。

4

2 に答える 2

4

問題は、それを2回装飾すると、1つは関数を返し、もう1つは何も返さないということです。

foo3() -> @selector.tag('a') -> foo3()
foo3() -> @selector.tag('b') -> do_nothing

foo3() -> @selector.tag('b') -> do_nothing
do_nothing -> @selector.tag('a') -> do_nothing

つまり、どのような順序でも、常に何も得られません。あなたがする必要があるのは、各オブジェクトにタグのセットを保持し、そのセット全体を一度にチェックすることです。関数属性で名前空間を汚染することなく、これをうまく行うことができます。

class Selector(set):
    def tag(self, *tags):
        tags = set(tags)
        def decorator(func):
            if hasattr(func, "_tags"):
                func._tags.update(tags)
            else:
                func._tags = tags
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                return func(*args, **kwargs) if self & func._tags else None
            wrapper._tags = func._tags
            return wrapper
        return decorator

これにはいくつかのボーナスがあります-関数が持つすべてのタグを検査することが可能であり、複数のデコレータでタグ付けするか、単一のデコレータで多くのタグを与えることが可能です。

@selector.tag('a')
@selector.tag('b')
def foo():
    ...


#Or, equivalently:
@selector.tag('a', 'b')
def foo():
    ...

の使用はfunctools.wraps()、関数が元の「ID」(docstring、nameなど)を保持することも意味します。

編集:ラッパーを削除したい場合:

    def decorator(func):
        if hasattr(func, "_tagged_function"):
            func = func._tagged_function
        if hasattr(func, "_tags"):
            func._tags.update(tags)
        else:
            func._tags = tags
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs) if self & func._tags else None
        wrapper._tagged_function = func
        wrapper._tags = func._tags
        return wrapper
于 2013-01-28T18:18:29.477 に答える
1

それはあなたのために働きますか:

class Selector(set):
    def tag(self, tag_list):
        def decorator(func):
            if set(tag_list) & self:
                return func
            else:
                return do_nothing
        return decorator


@selector.tag(['a','b'])
def foo3():
    print "I want to be called, but I won't be"
于 2013-01-28T18:29:08.733 に答える