gpt4 book ai didi

python - Python3 中自动定义的数值运算符

转载 作者:行者123 更新时间:2023-11-28 17:04:33 25 4
gpt4 key购买 nike

我正在尝试创建一个行为类似于整型数字类型的自定义类。最直接的方法是咨询 Python3 documentation就事论事,然后实现所有的魔法函数:既算术__add__, __radd__, __sub__, ...与比较__le__, __lt__, ...运营商。

但是,手动实现它们非常乏味。我相信在很多情况下,一个足够好的解决方案是基于 __int__ 自动实现它们。魔法函数:我们只需将对象转换为 int 进行算术运算。这是任何常用库的一部分吗?我要找的是类似于 @functools.total_ordering 的东西, 它会自动从一个运算符中派生出所有比较运算符。

如果没有这样的东西,那为什么呢?进行这种自动转换是不是一个坏主意,或者这只是一个很少遇到的问题?

编辑:如果自定义类的详细信息是相关的,我在这里提供了一些。

我正在构造的是一个计数器,它的值不仅可以通过算术运算改变,还可以通过其他方式改变。这些可以是非常通用的:例如,我们可以告诉计数器如下:“如果有人问你你代表什么值,返回正常值的两倍。”

举例来说,假设您正在玩类似于《文明》的棋盘游戏。游戏使用了很多部分,其中之一就是计算你的文明拥有的军事实力的计数器。但是,游戏中可能存在导致每个力量点计算两次的效果。

由于类必须是可变的,我认为子类化 int 不是一个选项(因为 int 是不可变的)。

最佳答案

您可以编写自己的混合类或装饰器来执行此操作,类似于 collections.abc.Sequence 在少数几个方法之上实现一堆 Sequence 方法的方式您手动编写,或者 functools.total_ordering 为您实现一堆比较方法。 (如果您想知道如何决定使用哪一个:如果代码必须在修改您的类之前检查它,就像 total_ordering 一样,您的 mixin 将需要一个自定义元类,因此装饰器更容易. 否则,mixin 通常更简单。)

围绕 PyPI 和 ActiveState 配方以及随机博客文章和 GitHub 存储库,有数十种此想法的实现。但是这个问题并不是很普遍,无法将解决方案转化为广泛使用的库。

至于你想要的具体情况——一个像可变int对象,并在其__int__方法的基础上实现它的对象——我认为具体情况只是没有经常出现,以至于没有人坐下来写下来。


无论如何,让所有这些都正确——包括像 3.0 + xx + 3.0 这样的东西——有点痛苦。

Implementing the arithmetic operationsnumbers 文档中,准确解释了您要如何做才能做到正确。

它还包含对 fractions 使用的源代码的解释。库来减少在 fraction.Fraction 上定义所有这些方法的样板和重复,这非常方便。

所有这些的唯一问题是它是为不可变数字类型而不是可变数字类型编写的。所以,如果你想使用它,你需要修改它,使用 operator_fallback 函数构建并返回 forward, reverse, inplace 而不仅仅是 正向、反向


但是,您的案例比 Fraction 简单,因此您可以简化运算符函数创建方法,如下所示:

def _operator_fallbacks(op, sym):
def forward(a, b):
result = op(int(a), b)
if isinstance(result, int): result = type(a)(result)
return result
forward.__name__ = '__' + op.__name__ + '__'
forward.__doc__ = f"a {sym} b"

def reverse(b, a):
result = op(a, int(b))
if isinstance(result, int): result = type(b)(result)
return result
reverse.__name__ = '__r' + op.__name__ + '__'
reverse.__doc__ = f"a {sym} b"

def inplace(a, b):
# If you want to work via __int__ rather than by digging
# into the internals, you can't delegate += to +=, you
# have to implement it via some kind of set method.
a.set(op(int(a), b))
return a
inplace.__name__ = '__i' + op.__name__ + '__'
inplace.__doc__ = f'a {sym}= b'

return forward, reverse, inplace

__add__, __radd__, __iadd__ = _operator_fallbacks(operator.add, '+')
__sub__, __rsub__, __isub__ = _operator_fallbacks(operator.sub, '-')

您可能需要考虑 isinstance(result, numbers.Integral)。您可能还想让您的类成为 numbers.Integral 的子类型。如果两者都做,请注意如何构建它们,这样您就不会得到 Int(Int(Int(5))) 而不是 Int(5).

如果您想将其概括为可用于可变 intfloatcomplexDecimalFraction 等类型,您可以只添加一个 get 方法,您的所有类型都必须实现该方法,而不是依赖于 __int__。尽管您必须考虑转换问题(int 很简单,位于数字塔的基础上)。

关于python - Python3 中自动定义的数值运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51912094/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com