Python 3では、(1回のパスしか許されないイテレータではなく)オブジェクトがコンテナであるかどうかをどのように確認できますか?ここでイテラブルが複数のパスを許可するかどうかをチェックする方法?
は例です:
明らかdef renormalize(cont):
'''
each value from the original container is scaled by the same factor
such that their total becomes 1.0
'''
total = sum(cont)
for v in cont:
yield v/total
list(renormalize(range(5))) # [0.0, 0.1, 0.2, 0.3, 0.4]
list(renormalize(k for k in range(5))) # [] - a bug!
、renormalize
関数はジェネレータ式を受信したときに意図したとおり、それは動作しません。コンテナを複数回反復することができますが、ジェネレータは1回だけ通過することができます。
理想的には、私はこれを行うにはしたいと思います:
def renormalize(cont):
if not is_container(cont):
raise ContainerExpectedException
# ...
どのように実装することができis_container
?
私は、引数が空であるかどうかをチェックすることができます。私たちは2番目のパスを開始するようになっています。しかし、このアプローチは、2回目のパスが正確に始まるときにはっきりしない、より複雑な関数に対しては機能しません。さらに、私はむしろ、機能の深い部分ではなく、関数の入口にバリデーションを置くことにします(関数が変更されたときはいつでもそれをシフトします)。
もちろん、renormalize
関数を1パスのイテレータで正しく動作するように書き換えることができます。しかし、それは入力データをコンテナにコピーする必要があります。何百万もの大規模なリストを「リストではない場合に備えて」コピーすることによるパフォーマンスへの影響はばかげています。
EDIT:私のオリジナルの例では、weighted_average
機能使用:
def weighted_average(c):
'''
returns weighted average of a container c
c contains values and weights in tuples
weights don't need to sum up 1 (automatically renormalized)
'''
return sum((v * w for v, w in c))/sum((w for v, w in c))
weighted_average([(0,1), (1,1)]) #0.5
weighted_average([(k, 1) for k in range(2)]) #0.5
weighted_average((k, 1) for k in range(2)) #mistake
をしかし、単一のパスを使用するようにリライトweighted_average
のバージョンがとにかく間違いなく優れているので、それが最良の例ではありませんでした。
def weighted_average(it):
'''
returns weighted average of an iterator it
it yields values and weights in tuples
weights don't need to sum up 1 (automatically renormalized)
'''
total_value = 0
total_weight = 0
for v, w in it:
total_value += v
total_weight += w
return total_value/total_weight
一般的なバージョンの問題は表示されません。プロファイルを作成しましたか?視覚的な複雑さはどういう意味ですか? – LBarret
「正確に2回目のパスが始まるのは明白ではありませんか?これはどういう意味ですか? 'itertoolsを使うことができます。tee() 'を使用して、必要な回数だけ反復できることを無条件に保証します。アルゴリズムを設計しているとき、どうしてそれが明らかでないのでしょうか? –
@LionelBarret:一般的な 'weighted_average'を使わない理由はないと思います。私は別の例を与えるために質問を更新しました。 – max