我使用的是一个Python对象,它实现了__add__
,但不包含int
的子类。MyObj1 + MyObj2
工作正常,但sum([MyObj1, MyObj2])
导致TypeError
,因为sum()
首先尝试0 + MyObj
。为了使用sum()
,我的对象需要__radd__
来处理MyObj + 0
或我需要提供一个空对象作为start
参数。有问题的对象不是设计为空的。
在有人询问之前,对象不是列表式或字符串式的,所以使用join()或itertools没有帮助。
编辑以获取详细信息:模块有一个SimpleLocation和一个CompoundLocation。我将Location缩写为Loc。一个SimpleLoc
包含一个右开区间,即[start,end)。加上SimpleLoc
得到一个CompoundLoc
,它包含一个区间列表,例如[[3, 6), [10, 13)]
。最终用途包括遍历并集,如[3, 4, 5, 10, 11, 12]
、检查长度和检查成员关系。
数字可以相对较大(例如,小于2^32,但通常为2^20)。间隔时间可能不会很长(100-2000,但可能更长)。目前,只存储端点。我现在正在尝试尝试对set
进行子类化,以便将位置构造为set(xrange(start, end))
。然而,添加集合将使Python(和数学家)适合。
我看过的问题:
- python';sum()和非整数值
- 为什么有';s是python中的起始参数';s内置求和函数
- 重写__add__方法后的TypeError
我正在考虑两种解决方案。一种是避免使用sum()
,并使用此注释中提供的循环。我不明白为什么sum()
一开始就把可迭代的第0项添加到0,而不是添加第0项和第1项(就像链接评论中的循环);我希望有一个神秘的整数优化的原因。
我的另一个解决方案如下;虽然我不喜欢硬编码的零检查,但这是我使sum()
工作的唯一方法。
# ...
def __radd__(self, other):
# This allows sum() to work (the default start value is zero)
if other == 0:
return self
return self.__add__(other)
总之,对于既不能添加到整数也不能为空的对象,是否有其他方法可以使用sum()
?
使用:而不是sum
import operator
from functools import reduce
reduce(operator.add, seq)
在Python 2中,reduce
是内置的,所以看起来像:
import operator
reduce(operator.add, seq)
Reduce通常比sum更灵活-您可以提供任何二进制函数,而不仅仅是add
,并且您可以可选地提供初始元素,而sum
始终使用一个。
另请注意:(警告:前方数学咆哮)
从代数的角度来看,为没有中性元素的add
w/r/t对象提供支持有点尴尬。
注意所有:
- 自然的
- 雷亚尔
- 复数
- N-d矢量
- NxM矩阵
- 字符串
与加法一起形成一个单体-即它们是结合的,并具有某种中性元素。
如果您的操作不是关联的,并且没有中性元素,那么它就不"类似"加法因此,不要指望它能很好地与sum
配合使用。
在这种情况下,使用函数或方法而不是运算符可能会更好。这可能不那么令人困惑,因为您的类的用户看到它支持+
,可能会期望它将以一种monoid的方式运行(就像加法通常所做的那样)。
感谢您的扩展,我现在将参考您的特定模块:
这里有两个概念:
- 简单的位置
- 大院位置
可以添加简单的位置,这确实是有道理的,但它们不形成单半群,因为它们的添加不满足闭包的基本性质——两个SimpleLoc的和不是SimpleLoc。一般来说,它是一个CompoundLoc。
OTOH,带加法的CompoundLocs在我看来像是一个monoid(一个交换的monoid,当我们在做它的时候):这些的和也是一个CompoundLoc,它们的加法是结合的,交换的,并且中性元素是一个空的CompoundLoc,它包含零个SimpleLocs
如果你同意我的观点(并且上面的内容与你的实现相匹配),那么你就可以使用sum
如下:
sum( [SimpleLoc1, SimpleLoc2, SimpleLoc3], start=ComplexLoc() )
事实上,这似乎奏效了。
我现在正在尝试尝试对set进行子类化,以便将位置构造为set(xrange(start,end))。然而,添加集合将使Python(和数学家)适合。
位置是一些数字集,所以在它们上面抛出一个类似集合的接口是有意义的(所以__contains__
、__iter__
、__len__
,也许__or__
是+
的别名,__and__
是乘积,等等)。
至于xrange
的施工,你真的需要吗?如果您知道您正在存储间隔集,那么您可能会通过坚持[start, end)
对的表示来节省空间。如果你觉得有用的话,你可以引入一个实用方法,它可以获取任意的整数序列,并将其转换为最佳的SimpleLoc
或CompoundLoc
。
我认为实现这一的最佳方法是提供__radd__
方法,或者显式地传递开始对象求和。
如果您真的不想覆盖__radd__
或提供启动对象,那么重新定义sum()
如何?
>>> from __builtin__ import sum as builtin_sum
>>> def sum(iterable, startobj=MyCustomStartObject):
... return builtin_sum(iterable, startobj)
...
最好使用一个名为my_sum()
的函数,但我想这是你想要避免的事情之一(尽管全局重新定义内置函数可能是未来维护人员诅咒你的事情)
实际上,在没有"空对象"概念的情况下实现__add__
没有什么意义。sum
需要一个start
参数来支持空序列和单元素序列的和,并且您必须决定在这些情况下预期的结果:
sum([o1, o2]) => o1 + o2 # obviously
sum([o1]) => o1 # But how should __add__ be called here? Not at all?
sum([]) => ? # What now?
您可以使用一个通用中性的对象
class Neutral:
def __add__(self, other):
return other
print(sum("A BC D EFG".split(), Neutral())) # ABCDEFG
您可以这样做:
from operator import add
try:
total = reduce(add, whatever) # or functools.reduce in Py3.x
except TypeError as e:
# I'm not 100% happy about branching on the exception text, but
# figure this msg isn't likely to be changed after so long...
if e.args[0] == 'reduce() of empty sequence with no initial value':
pass # do something appropriate here if necessary
else:
pass # Most likely that + isn't usable between objects...