【问题标题】:Override multiple methods with a wrapper (Python 3)使用包装器覆盖多个方法(Python 3)
【发布时间】:2013-06-11 21:32:28
【问题描述】:

为了继承 float 并使用包装器覆盖多个数字运算,我查看了 this example 并尝试了以下操作:

def naturalize(*methods):
  def decorate(cls):
    for method in methods:
      method = '__' + method + '__'
      original = getattr(cls.__base__, method)
      setattr(cls, method, lambda self, *args, **kwargs: cls(original(self, *args, **kwargs)))
    return cls
  return decorate

@naturalize('add', 'sub', 'mul')
class P(float):
  pass

print('Test result:', P(.1) + .2, P(.1) - .2, P(.1) * .2)
# Test result: 0.020000000000000004 0.020000000000000004 0.020000000000000004

这不起作用:__add____sub____mul__ 都像 __mul__ 一样工作。于是我查看了this other example 并尝试了:

def naturalize(*methods):
  def decorate(cls):
    def native(method):
      original = getattr(cls.__base__, method)
      return lambda self, *args, **kwargs: cls(original(self, *args, **kwargs))
    for method in methods:
      method = '__' + method + '__'
      setattr(cls, method, native(method))
    return cls
  return decorate

@naturalize('add', 'sub', 'mul')
class P(float):
  pass

print('Test result:', P(.1) + .2, P(.1) - .2, P(.1) * .2)
#Test result: 0.30000000000000004 -0.1 0.020000000000000004

现在,这确实奏效了。但我仍然不确定我的第一种方法到底出了什么问题。谁能向我解释一下__add____sub____mul__ 到底是如何像__mul__ 一样工作的?

【问题讨论】:

    标签: python class inheritance overriding wrapper


    【解决方案1】:

    在您的第一个示例中,所有lambdas 具有相同的范围,这意味着当method 发生更改时,它会反映在所有lambdas 中。将它嵌套在第二个示例中的函数中会为每次调用创建一个新范围,从而将 lambdas 彼此隔离。

    【讨论】:

      【解决方案2】:

      试试这个调试版本的naturalize

      def naturalize(*methods):
          def decorate(cls):
              for method in methods:
                  method = '__' + method + '__'
                  original = getattr(cls.__base__, method)
                  setattr(cls, method,
                          lambda self, *args, **kwargs:
                          (cls(original(self, *args, **kwargs)), original.__name__))
              return cls
          return decorate
      

      你会得到这样的输出:

      Test result: (0.020000000000000004, '__mul__') (0.020000000000000004, '__mul__') (0.020000000000000004, '__mul__')
      

      所以我们看到 original 总是等于 __mul__ 在调用装饰方法时。确实,当装饰方法被调用时,

      cls(original(self, *args, **kwargs))
      

      被评估。由于originaldecorate 中的局部变量,它保留了for-loop 结束后的last 值。所以original 总是计算为__mul__

      正确的版本,

      def naturalize(*methods):
          def decorate(cls):
              def native(method):
                  original = getattr(cls.__base__, method)
                  return lambda self, *args, **kwargs: cls(original(self, *args, **kwargs))
              for method in methods:
                  method = '__' + method + '__'
                  setattr(cls, method, native(method))
              return cls
          return decorate
      

      通过将original 定义为native 中的局部变量来避免此问题。现在,通过调用native(method) 为每个method,内部native original 绑定一次到每个所需的原始值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-28
        • 1970-01-01
        • 2021-10-04
        • 1970-01-01
        • 2012-04-24
        • 2011-10-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多