用最少的代码实现 numbers.Real

Posted

技术标签:

【中文标题】用最少的代码实现 numbers.Real【英文标题】:Implementing numbers.Real with minimal code 【发布时间】:2020-04-01 06:56:44 【问题描述】:

我正在尝试创建一个类MyFloat,它与float 非常相似,但有一个value 方法和一些其他方法。我还需要这个类的实例来与真正的floats 进行交互,并且在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协议

实现深拷贝最少得用几行代码?

华为OD机试真题Python实现删除最少字符真题+解题思路+代码(2022&2023)

华为机试题 HJ23删除字符串中出现次数最少的字符

LRU(最近最少使用淘汰算法)基本实现