1

为了float使用包装器对多个数字操作进行子类化和覆盖,我查看了这个示例并尝试了以下操作:

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__. 因此,我查看了另一个示例并尝试了:

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__吗?

4

2 回答 2

0

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

于 2013-06-15T09:42:12.617 回答
0

试试这个调试版本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))

被评估。由于original是 中的局部变量decorate,因此它保留结束后的最后一个for-loop。所以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)each method,内部nativeoriginal 被绑定到每个所需的原始值。

于 2013-06-15T09:42:24.757 回答