我正在使用实现__add__
的Python对象,但不是int
的子类。 MyObj1 + MyObj2
工作正常,但sum([MyObj1, MyObj2])
导致TypeError
,因为sum()
首次尝试0 + MyObj
。要使用sum()
,我的对象需要__radd__
来处理MyObj + 0
或我需要提供一个空对象作为start
参数。有问题的对象不是空的。
在有人要求之前,该对象不像列表或类似字符串,因此使用join()或itertools无济于事。
编辑以获取详细信息:模块具有SimpleLocation和CompoundLocation。我将把位置缩写为Loc。 SimpleLoc
包含一个右开间隔,即[开始,结束]。添加SimpleLoc
会产生CompoundLoc
,其中包含间隔列表,例如[[3, 6), [10, 13)]
。最终用途包括遍历联合,例如[3, 4, 5, 10, 11, 12]
,检查长度和检查会员资格。
数字可能相对较大(例如,小于2 ^ 32但通常为2 ^ 20)。间隔可能不会非常长(100-2000,但可能更长)。目前,仅存储端点。我现在暂时想要尝试子类set
,以便将位置构造为set(xrange(start, end))
。但是,添加集将使Python(和数学家)适合。
我看过的问题:
我正在考虑两种解决方案。一种是避免sum()
并使用此comment中提供的循环。我不明白为什么sum()
开始时将iterable的第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()
既不能添加到整数也不能为空?
答案 0 :(得分:10)
使用:
而不是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对象提供支持有点尴尬。
请注意以下所有内容:
加上形成Monoid - 即它们是关联的并且具有某种中性元素。
如果你的操作不是关联的,并且没有中性元素,那么它就不会“类似”添加。因此,不要指望它与{{1 }}
在这种情况下,使用函数或方法而不是运算符可能会更好。这可能不那么令人困惑,因为类的用户看到它支持sum
,可能会期望它会以一种单调的方式运行(正如加法通常那样)。
感谢您的扩展,我现在将参考您的特定模块:
这里有两个概念:
确实可以添加简单的位置,但它们不会形成一个monoid,因为它们的加法不满足闭包的基本属性 - 两个SimpleLocs的总和不是SimpleLoc。通常,它是一个CompoundLoc。
如果您同意我(以及上述内容与您的实施相符),那么您将能够使用+
,如下所示:
sum
确实,appears to work。
我现在暂时想到尝试子集set,使得该位置被构造为set(xrange(start,end))。但是,添加集将使Python(和数学家)适合。
嗯,位置是一些数字集合,所以在它们之上抛出一个类似于集合的界面是有意义的(所以sum( [SimpleLoc1, SimpleLoc2, SimpleLoc3], start=ComplexLoc() )
,__contains__
,__iter__
,也许是{{ 1}}作为__len__
,__or__
的别名作为产品等。)
至于+
的建筑,你真的需要吗?如果您知道存储了多组间隔,那么您可以通过坚持__and__
对的表示来节省空间。您可以引入一个实用程序方法,该方法采用任意整数序列并将其转换为最佳xrange
或[start, end)
,如果您觉得它会有所帮助。
答案 1 :(得分:4)
我认为完成此的最佳方法是以提供__radd__
方法,或者将start对象明确地传递给sum。
如果你真的不想覆盖__radd__
或提供开始对象,那么如何重新定义sum()
?
>>> from __builtin__ import sum as builtin_sum
>>> def sum(iterable, startobj=MyCustomStartObject):
... return builtin_sum(iterable, startobj)
...
最好使用名称为my_sum()
的函数,但我想这是你想要避免的事情之一(即使全局重新定义内置函数可能是未来维护者会诅咒你的事情)< / p>
答案 2 :(得分:3)
实际上,在没有“空对象”概念的情况下实现__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?
答案 3 :(得分:2)
你可以使用普遍中立的对象。此外:
class Neutral:
def __add__(self, other):
return other
print(sum("A BC D EFG".split(), Neutral())) # ABCDEFG
答案 4 :(得分:0)
你可以这样:
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...