0

私は以下のように微調整されたバージョンを使用してデコレータについてもっと学ぼうとしています:ここから

def case(comparision):
    def __assign_case(f):
        f.__case = comparision
        return f
    return __assign_case

class switch:
    def __init__(self):
        self.__case_map = {}

        def set_case(key,f):
            self.__case_map[key] = f

        a = [getattr(self,e) for e in dir(self) if getattr(self,e) is not None and hasattr(getattr(self,e),'__case')]
        for f in a:
            cases = getattr(f,'__case')
            if isinstance(cases,tuple) or isinstance(cases,list):
                for c in cases: set_case(c,f)
            else:
                set_case(cases,f)
        print(self.__case_map)


    def match(self,value):
        try:
            self.__case_map[value]
        except KeyError:
            return self.__case_map['_default']
        return self.__case_map[value]


class b(switch):
    @case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')

    @case(2)
    def event_two(self):
        print('Event handler for 2 in b')

    @case('_default')
    def default(self):
        print('No match was found, using default case')



a = b()
a.match(1)()
a.match(2)()
a.match(5)()

の実行結果:

$ ./switch_decor.py 
{1: <bound method b.event_one of <__main__.b object at 0x7f03374849d0>>, '_default': <bound method b.default of <__main__.b object at 0x7f03374849d0>>, 3: <bound method b.event_one of <__main__.b object at 0x7f03374849d0>>, 2: <bound method b.event_two of <__main__.b object at 0x7f03374849d0>>}
Event handler for 1,3 in b
Event handler for 2 in b
No match was found, using default case

いっぱいになった辞書に注目してください

私は自分のコードを封じ込めておくのが好きなので、次のようにcase関数をswitchクラスに移動しようとしていました。

class switch:
    def __init__(self):
        self.__case_map = {}

        def set_case(key,f):
            self.__case_map[key] = f

        a = [getattr(self,e) for e in dir(self) if getattr(self,e) is not None and hasattr(getattr(self,e),'__case')]
        for f in a:
            cases = getattr(f,'__case')
            if isinstance(cases,tuple) or isinstance(cases,list):
                for c in cases: set_case(c,f)
            else:
                set_case(cases,f)
        print(self.__case_map)


    def match(self,value):
        try:
            self.__case_map[value]
        except KeyError:
            return self.__case_map['_default']
        return self.__case_map[value]

    @staticmethod
    def case(comparision):
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case

class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')

    @switch.case(2)
    def event_two(self):
        print('Event handler for 2 in b')

    @switch.case('_default')
    def default(self):
        print('No match was found, using default case')



a = b()
a.match(1)()
a.match(2)()
a.match(5)()

しかし、私は空のself.__case_map辞書を取得することになり、このエラーが発生します:

$ ./switch_decor_contained.py
{}
Traceback (most recent call last):
  File "./switch_decor_contained.py", line 23, in match
    self.__case_map[value]
KeyError: 1

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./switch_decor_contained.py", line 50, in <module>
    a.match(1)()
  File "./switch_decor_contained.py", line 25, in match
    return self.__case_map['_default']
KeyError: '_default'

最初の空白の辞書に注意してください。で失敗します

a.match(1)

その前にreturn関数が呼び出され、2番目()の関数は空白で例外をトリップする_default関数ですが、返す関数値へのキーはありません。

上記の最初のコードセットでは、これをコンソールで実行すると次のようになります。

>>> hasattr(a.event_one, "__case")
True

しかし、上記の2番目のコードセットでは、

>>> hasattr(a.event_one, "__case")
False

@switch.caseその上でテストが示したように実行されているという事実にもかかわらず。クラス内で呼び出すと、いくつかの厄介な副作用があります。副作用は何ですか?

case関数をswitchクラスに移動し、それをメソッドとして使用して、クラス内の関数を正常に装飾するにはどうすればよいbですか?

4

2 に答える 2

2

まず、を呼び出そうとしている場合は、メソッドへのパラメーターがself.case必要です。デコレータとして使用しているのか、通常の機能として使用しているのかは関係ありません。それはまだ同じように呼ばれています。selfcasecase

bしかし、もっと重要なことは、クラスを定義しているときには、はありませんself。考えてみると、の各インスタンスを参照しているため、インスタンスが存在する可能性はありません。インスタンスはまだ存在しません。クラスもまだ存在しません。別の角度から見ると、これまでにaを取得する唯一の方法は、パラメーターとして1つを取ることです。を定義している時点では、スコープはありません。(Pythonを初めて使用する人の多くは、魔法がたくさんあると思っていますが、調べてから呼ばれるという事実以外に何もありません。)selfbselfselfbselffoo.bar(baz)foobar(foo, baz)

カプセル化の目的で関数をクラスに移動したいが、それをインスタンスメソッドにしたくない場合、それを行う方法は次のstaticmethodとおりです。

class switch:
    # ...
    @staticmethod
    def case(comparision):
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case

実際、あなたが望むことはそれほど一般的ではありませんstaticmethod; classmethodオブジェクトへのアクセスが必要なため、通常はが必要ですclass(したがって、他のを呼び出しclassmethodたり、クラス属性にアクセスしたり、のインスタンスを作成したりできますclass)。しかし、この場合、あなたはbまだクラスを持っていません、そしてあなたが持っていたとしてもあなたはそれで何もするのに役立つものを持っていないでしょう、それであなたstaticmethodはまさにあなたが望むものです。(PS、ここにアクセスしたくないのは良いことです。そうしないbと、メタクラスについて学ぶ必要があります…そして、最初にデコレータのコツをつかむまで待ちます。)

最後に、デコレータをデコレータ定義に入れていますが(これは非常に一般的なことです)、ほとんどの場合functools.wraps、デコレータで使用する必要があります。

class switch:
    # ...
    @staticmethod
    def case(comparision):
        @functools.wraps(comparision)
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case

とにかく、それがすべて邪魔にならないので、あなたが持っていない場合、あなたはどのように電話 しますか?静的メソッドは、それが含まれるクラス、任意のサブクラス、またはいずれかのインスタンスで呼び出すことができますが、通常はクラスで呼び出されます。caseself

class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')
于 2013-02-05T02:07:22.880 に答える
0

私は答えが問題だと思った。これは、クラスプライベート変数の属性の名前変更と関係があります。これは、Python3の場合はプライベート変数の下にあり、Python2の場合はセクション9.6プライベート変数とクラスローカル参照にあります。

関数をメソッドとして使用するコード(上記の2番目のリスト)では、のf.__case名前がに変更されましたf._switch__case。関数を除いてのすべてのインスタンス__caseがに置き換えられた場合、コードは正常に機能します。Pythonがこのように設計されている理由は100%わかりませんが、名前空間の保持と関係があると思いますが、独自の名前空間があるために必要な理由はわかりません。_switch__case@staticmethodclass

修正されたコードは次のとおりです。

class switch:
    def __init__(self):
        self.__case_map = {}

        def set_case(key,f):
            self.__case_map[key] = f

        a = [getattr(self,e) for e in dir(self) if getattr(self,e) is not None and hasattr(getattr(self,e),'_switch__case')]
        for f in a:
            cases = getattr(f,'_switch__case')
            if isinstance(cases,tuple) or isinstance(cases,list):
                for c in cases: set_case(c,f)
            else:
                set_case(cases,f)

    def match(self,value):
        try:
            self.__case_map[value]
        except KeyError:
            return self.__case_map['_default']
        return self.__case_map[value]

    @staticmethod
    def case(comparision):
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case

class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')

    @switch.case(2)
    def event_two(self):
        print('Event handler for 2 in b')

    @switch.case('_default')
    def default(self):
        print('No match was found, using default case')



a = b()
a.match(1)()
a.match(2)()
a.match(5)()
于 2013-02-05T07:03:12.230 に答える