用最少的代码实现 numbers.Real
Posted
技术标签:
【中文标题】用最少的代码实现 numbers.Real【英文标题】:Implementing numbers.Real with minimal code 【发布时间】:2020-04-01 06:56:44 【问题描述】:我正在尝试创建一个类MyFloat
,它与float
非常相似,但有一个value
方法和一些其他方法。我还需要这个类的实例来与真正的float
s 进行交互,并且在type(MyFloat(5) + 4)
是MyFloat
而不是float
的意义上是“粘性的”。
这就是我目前所拥有的:
class MyFloat(numbers.Real):
def __init__(self, value):
self._value = float(value)
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = float(value)
def __float__(self):
return self.value
但现在我必须实现大量其他方法才能满足抽象基类numbers.Real
以及我的要求,即它像float
一样“行动”。问题是a)有很多这些方法(__add__
、__trunc__
、__floor__
、__round__
、__floordiv__
...我认为总共有 24 个)和 b)它们都遵循相同的图案。也就是说,对于任何方法,我可能只想:
def __method__(self):
return MyFloat(float.__method__(float(self))
或
def __method__(self, other):
return MyFloat(float.__method__(float(self), float(other)))
有没有办法在不手动实现每个数学方法(并重复大量代码)的情况下做我想做的事?
请记住,float.__method__
实际上并不适用于每种方法。比如float.__floor__
不存在,但我还是需要实现MyFloat.__floor__
来满足numbers.Real
抽象基类。
编辑:建议是扩展float
,这也是我的第一个想法,但我失去了“粘性”要求。例如:
>>> class MyFloat(float):
def other_method(self):
print("other method output")
>>> mf = MyFloat(8)
>>> mf.other_method()
other method output
>>> o = mf + 9
>>> type(o)
<class 'float'>
>>> o.other_method()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'float' object has no attribute 'other_method'
【问题讨论】:
您是否在问是否有一种方法可以创建现有对象类型的副本,而无需手动实现所有方法? 您可以在循环中使用__getattr__
或exec
和字符串格式来做一些诡计。
@user1558604 不,我知道我可以从 float 继承,但我失去了我的“价值”方法和我的“粘性”要求。 (afaik)@MichaelButscher 我没碰exec
;-)
如果你想继承浮点数,为什么不实际这样做:class MyFloat(float):
,然后在初始化函数super().__init__(value)
。这样浮动类的所有属性和方法也在你的类中
如果你扩展float,你应该能够使用它的所有功能并在上面添加你自己的。
【参考方案1】:
好的,这是我想出的最“优雅”的解决方案(尽管它是对 hacks 的 hacks)。如果有人有改进建议,我很乐意选择您的作为正确答案。
import numbers
import math
import operator
def _operator_builder(numbers_base_cls, builtin_type):
def _binary_method_builder(builtin_operator):
def binary_method(a, b):
if isinstance(b, numbers_base_cls):
return type(a)(builtin_operator(builtin_type(a), builtin_type(b)))
else:
return NotImplemented
binary_method.__name__ = '__' + builtin_operator.__name__ + '__'
binary_method.__doc__ = operator.__doc__
return binary_method
def _monary_method_builder(builtin_operator):
def monary_method(a, *args):
return type(a)(builtin_operator(builtin_type(a), *args))
monary_method.__name__ = '__' + builtin_operator.__name__ + '__'
monary_method.__doc__ = operator.__doc__
return monary_method
return _binary_method_builder, _monary_method_builder
_float_binary_operator_helper, _float_monary_operator_helper = _operator_builder(numbers.Real, float)
class MyFloat(numbers.Real):
__add__ = _float_binary_operator_helper(operator.add)
__radd__ = __add__
__trunc__ = _float_monary_operator_helper(math.trunc)
__floor__ = _float_monary_operator_helper(math.floor)
__ceil__ = _float_monary_operator_helper(math.ceil)
__round__ = _float_monary_operator_helper(round)
# etc... for all dunder methods
def __init__(self, value):
self._value = float(value)
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = float(value)
def __float__(self):
return self.value
一项改进是定义一个 (name
, func
) 对列表,并在 __new__
期间对每个对调用 setattr(name, _float_monary_operator_helper(func))
。但是,我不知道如何做到这一点并让 ABC 高兴——它总是抱怨方法没有实现。
【讨论】:
以上是关于用最少的代码实现 numbers.Real的主要内容,如果未能解决你的问题,请参考以下文章
以最少的代码让自定的model实现NSCodingNSCopying协议