16

__add__を実装しているがサブクラス化していないPython オブジェクトを使用していintます。MyObj1 + MyObj2正常に動作しますがsum([MyObj1, MyObj2])TypeError最初sum()0 + MyObj. を使用するsum()には、オブジェクト__radd__を処理するMyObj + 0 、パラメーターとして空のオブジェクトを指定する必要がありstartます。問題のオブジェクトは空になるように設計されていません。

誰かが尋ねる前に、オブジェクトはリストのようなものでも文字列のようなものでもないので、join() や itertools を使用しても役に立ちません。

詳細を編集:モジュールには SimpleLocation と CompoundLocation があります。Location を Loc に略します。ASimpleLocには右開き区間が 1 つ、つまり [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 (および数学者) が適合します。

私が見た質問:

私は2つの解決策を考えています。1 つは、このコメントsum()で提供されているループを回避して使用することです。0番目と1番目のアイテムを追加するのではなく、イテラブルの0番目のアイテムを0に追加することから始まる理由がわかりません(リンクされたコメントのループのように); 難解な整数最適化の理由があることを願っています。sum()

私の他の解決策は次のとおりです。ハードコーディングされたゼロチェックは好きではありませんが、それが私が機能させることができた唯一の方法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()整数に追加することも空にすることもできないオブジェクトで使用する別の方法はありますか?

4

5 に答える 5

15

の代わりにsum、次を使用します。

import operator
from functools import reduce
reduce(operator.add, seq)

Python 2 ではreduce組み込みなので、次のようになります。

import operator
reduce(operator.add, seq)

Reduce は一般に sum よりも柔軟性がaddありますsum


また、注意してください:(警告:数学は先に暴言を吐きます)

ニュートラル要素を持たない w/r/t オブジェクトのサポートを提供するaddことは、代数の観点からは少し厄介です。

以下のすべてに注意してください。

  • ナチュラルズ
  • レアル
  • 複素数
  • Nd ベクトル
  • NxM 行列
  • 文字列

加算とともにモノイドを形成します。つまり、それらは連想的で、ある種の中立的な要素を持っています。

演算が連想的ではなく、ニュートラル要素を持たない場合、それは加算に「似ていません」。したがって、 でうまく機能するとは思わないでくださいsum

そのような場合、演算子の代わりに関数またはメソッドを使用した方がよい場合があります。あなたのクラスのユーザーは、それが をサポートしていることを見て、+(加算が通常行うように) モノイディックな方法で動作することを期待する可能性が高いため、これにより混乱が少なくなる可能性があります。


拡張していただきありがとうございます。特定のモジュールを参照します。

ここには 2 つの概念があります。

  • シンプルな場所、
  • 複合ロケーション。

単純な場所を追加できることは確かに理にかなっていますが、それらの追加はクロージャーの基本的なプロパティを満たさないため、モノイドを形成しません.2つのSimpleLocの合計はSimpleLocではありません。一般的には、CompoundLoc です。

OTOH、追加の CompoundLocs は私にはモノイドのように見えます (私たちがそれを行っている間は可換モノイドです): これらの合計も CompoundLoc であり、それらの追加は結合的で可換的であり、ニュートラル要素は空の CompoundLoc であり、ゼロ SimpleLocs。

私に同意する場合 (および上記が実装と一致する場合)、sum次のように使用できます。

sum( [SimpleLoc1, SimpleLoc2, SimpleLoc3], start=ComplexLoc() )

確かに、これは機能しているようです。


私は現在、場所が set(xrange(start, end)) として構築されるように set をサブクラス化しようと暫定的に考えています。ただし、セットを追加すると、Python (および数学者) が適合します。

場所は数のセットなので、それらの上にセットのようなインターフェイスを配置することは理にかなっています (したがって__contains____iter____len__、おそらく__or__のエイリアスとして+__and__製品などとして)。

からの施工xrangeですが、本当に必要ですか?間隔のセットを保存していることがわかっている場合は、[start, end)ペアの表現に固執することでスペースを節約できる可能性があります。整数の任意のシーケンスを取り、それを最適なものに変換するユーティリティ メソッドを投入できSimpleLocますCompoundLoc

于 2012-07-24T06:29:16.427 に答える
4

これを達成する最善の方法は、メソッドを提供する__radd__か、開始オブジェクトを明示的に sum に渡すことだと思います。

__radd__開始オブジェクトをオーバーライドまたは提供したくない場合は、再定義してみてはどうsum()でしょうか?

>>> from __builtin__ import sum as builtin_sum
>>> def sum(iterable, startobj=MyCustomStartObject):
...     return builtin_sum(iterable, startobj)
... 

のような名前の関数を使用するmy_sum()ことをお勧めしますが、それは避けたいことの 1 つだと思います (ただし、組み込み関数をグローバルに再定義することは、おそらく将来のメンテナーがあなたを呪うでしょう)。

于 2012-07-24T06:15:12.107 に答える
3

実際、__add__「空のオブジェクト」の概念なしで実装することはほとんど意味がありません。 sumには、空のシーケンスと 1 要素のシーケンスの合計をサポートするパラメーターが必要でありstart、これらの場合に期待する結果を決定する必要があります。

sum([o1, o2]) => o1 + o2  # obviously
sum([o1]) => o1  # But how should __add__ be called here?  Not at all?
sum([]) => ?  # What now?
于 2012-07-24T06:16:22.777 に答える
2

普遍的にニュートラルなオブジェクトを使用できます。添加:

class Neutral:
    def __add__(self, other):
        return other

print(sum("A BC D EFG".split(), Neutral())) # ABCDEFG
于 2012-07-24T07:36:43.223 に答える
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...
于 2012-07-24T06:35:53.157 に答える