包装调用Python类的方法

Bri*_*unt 5 python python-2.7

我想用相同的包装器在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
Run Code Online (Sandbox Code Playgroud)

当然,当有更多的方法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()
Run Code Online (Sandbox Code Playgroud)

我想过使用__getattr__在混合料搅拌,但由于方法比如课程的ab已经被定义,这是永远不会被调用.

我也想过使用__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
Run Code Online (Sandbox Code Playgroud)

在我看来,Python中可能有一些内容可能会减少手动重现要包装的父类的每个方法的需要.或者关闭__getattribute__是正确的方法来做到这一点.我很感激你的想法.

Aya*_*Aya 4

这是我的尝试,它允许更简洁的语法......

x = 0 # some arbitrary context

# Define a simple function to return a wrapped class
def wrap_class(base, towrap):
    class ClassWrapper(base):
        def __getattribute__(self, name):
            original = base.__getattribute__(self, name)
            if name in towrap:
                def func_wrapper(*args, **kwargs):
                    global x
                    x = 1
                    try:
                        return original(*args, **kwargs)
                    finally:
                        x = 0
                return func_wrapper
            return original
    return ClassWrapper


# Our existing base class
class Base(object):
    def a(self):
       print "a x: %s" % x

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


# Create a wrapped class in one line, without needing to define a new class
# for each class you want to wrap.
Wrapped = wrap_class(Base, ('a',))

# Now use it
m = Wrapped()
m.a()
m.b()

# ...or do it in one line...
m = wrap_class(Base, ('a',))()
Run Code Online (Sandbox Code Playgroud)

...输出...

a x: 1
b x: 0
Run Code Online (Sandbox Code Playgroud)

  • @BrianM.Hunt 是的。接受的答案中的猴子修补方法对我来说似乎有点矫枉过正,尽管可能会有一点性能优势。无论哪种方式,如果您手动指定要包装的“属性”子集,那么“检查”内容几乎肯定是不必要的。 (2认同)