6

同じラッパーで Python のいくつかのクラス メソッドをラップしたいと思います。

概念的には、最も単純なシナリオでは次のようになります。

x = 0 # some arbitrary context

class Base(object):
    def a(self):
       print "a x: %s" % x

    def b(self):
       print "b x: %s" % x

 class MixinWithX(Base):
     """Wrap"""
     def a(self):
         global x
         x = 1
         super(MixinWithX, self).a()
         x = 0

     def b(self):
         global x
         x = 1
         super(MixinWithX, self).a()
         x = 0

もちろん、aとよりも多くのメソッドがある場合b、これはめちゃくちゃになります。もっとシンプルなものが必要なようです。明らかにxデコレータで変更できますが、それでも長いゴミのリストができてしまい、上記の代わりに次のようになります。

 from functools import wraps
 def withx(f):
     @wraps(f) # good practice
     def wrapped(*args, **kwargs):
         global x
         x = 1
         f(*args, **kwargs)
         x = 0
     return wrapped

 class MixinWithX(Base):
     """Wrap"""
     @withx
     def a(self):
         super(MixinWithX, self).a()

     @withx
     def b(self):
         super(MixinWithX, self).b()

ミックスインで使おうと思っ__getattr__たのですが、もちろんaやなどのメソッドbは定義済みなので、これが呼び出されることはありません。

使用することも考えまし__getattribute__たが、呼び出しをラップするのではなく、属性を返します。__getattribute__クロージャーを返すことができると思いますが(以下の例)、それがどの程度健全な設計であるかはわかりません。次に例を示します。

 class MixinWithX(Base):
    # a list of the methods of our parent class (Base) that are wrapped
    wrapped = ['a', 'b']

    # application of the wrapper around the methods specified
    def __getattribute__(self, name):
       original = object.__getattribute__(self, name)
       if name in wrapped:
          def wrapped(self, *args, **kwargs):
              global x
              x = 1 # in this example, a context manager would be handy.
              ret = original(*args, **kwargs)
              x = 0
              return ret
          return wrapped
       return original

ラップされる親クラスのすべてのメソッドを手動で再作成する必要性を軽減する何かが Python に組み込まれている可能性があることに気づきました。または、これを行う適切な方法は でのクロージャかもしれません__getattribute__。私は考えに感謝します。

4

4 に答える 4

1

解決策があり、それはデコレータと呼ばれます。多くの情報については、Google の「python デコレータ」を参照してください。

基本的な概念は、デコレータは関数をパラメーターとして受け取り、関数を返す関数であるということです。

def decorate_with_x(f)
    def inner(self):
         self.x = 1 #you must always use self to refer to member variables, even if you're not decorating
         f(self)
         self.x = 0
    return inner

class Foo(object):

     @decorate_with_x # @-syntax passes the function defined on next line
                      # to the function named s.t. it is equivalent to 
                      # foo_func = decorate_with_x(foo_func)
     def foo_func(self):
         pass
于 2013-05-22T17:06:29.993 に答える