まず、type(seq)( f(x) for x in seq )
本当にただtype(seq)(imap(f, seq))
です。なぜそれを使用しないのですか?
第二に、あなたがやろうとしていることは、一般的に意味をなさない. シーケンスだけでなく、任意のiterablemap
を取ります。違いは、基本的に、シーケンスにはと があり、ランダムにアクセスできることです。len
X 型の iterable を Y 型の値から を呼び出して構築できるという規則はありませんtype(X)(y_iter)
。実際、これは一般にシーケンスには当てはまりますが、それが当てはまる他の例はほとんどありません。
いくつかの特別なタイプを特別に処理したい場合は、次のようにできます。
def map2(f, seq):
it = imap(f, seq)
if isinstance(seq, (tuple, list)):
return type(seq)(it)
else:
return it
または、すべてのシーケンスがこの方法で構築できると仮定したい場合 (これはほとんどの組み込みシーケンスに当てはまりますが、たとえばxrange
— シーケンスとして設計されていないがプロトコルを満たしていることを考慮してください — そしてもちろん、組み込まれている以上の保証はありません):
def map2(f, seq):
it = imap(f, seq)
try:
len(seq)
except:
return it
else:
return type(seq)(it)
イテラブルから構築できるイテラブル型はすべてシーケンスであると想定できますが(質問で提案したように)...しかし、これは利点よりも誤検知につながる可能性が高いため、そうしません。len
繰り返しますが、これはシーケンスであることの定義の一部ですが、「イテレータから構築可能」はそうではなく、イテレータを指定するとまったく異なることを行う完全に合理的なイテラブル型が存在することを思い出してください。
あなたが何をしようと、それはハックになるでしょう。なぜなら、その意図はハックであり、Python 開発者の明確な設計上の希望に反するからです。イテレータ/イテラブル プロトコルの要点は、イテラブルの型をできるだけ気にしないことです。そのため、Python 3.x はさらに進んで、 や などのリストベースの関数を代わりに反復子ベースの関数に置き換えましmap
たfilter
。
では、これらの変換の 1 つをどのようにデコレータに変換するのでしょうか?
imap
まず、デコレータ ビットを飛ばして、 のような関数を受け取り、この変換が適用された同等の関数を返す高階関数を書きましょう。
def sequify(func):
def wrapped(f, seq):
it = func(f, seq)
try:
len(seq)
except:
return it
else:
return type(seq)(it)
return wrapped
そう:
>>> seqmap = sequify(itertools.imap)
>>> seqmap(int, (1.2, 2.3))
(1, 2)
>>> sequify(itertools.ifilter)(lambda x: x>0, (-2, -1, 0, 1, 2))
(1, 2)
では、これをどのようにデコレータに変換するのでしょうか? まあ、すでに関数を返す関数はデコレータです。おそらく追加したいでしょうがfunctools.wraps
(非デコレータの場合でも追加したいかもしれませんが)、それが唯一の変更点です。たとえば、imap のように動作するジェネレーター、またはイテレーターを返す関数を作成し、自動的に seqmap のような関数に変換することができます。
@sequify
def map_and_discard_none(func, it):
for elem in imap(func, it):
if elem is not None:
yield elem
今:
>>> map_and_discard_none(lambda x: x*2 if x else x, (1, 2, None))
(2, 4)
もちろん、これは - のような構文を持つ関数に対してのみ機能しmap
ます。つまり、関数と iterable を取ります。(まあ、さまざまな種類の間違った型を取る関数に対して誤って機能することがあります。たとえば、呼び出すことができsequify(itertools.count(10, 5))
、それがシーケンスではないことを正常に検出し5
、イテレータをそのまま返すだけです。) より一般的にするには、次のようにします。次のようなことができます:
def sequify(func, type_arg=1):
def wrapped(*args, **kwargs):
it = func(f, seq)
try:
len(args[type_arg])
except:
return it
else:
return type(seq)(it)
return wrapped
そして今、あなたは好きなものに夢中になることができsequify(itertools.combinations, 0)
ます。この場合、それを便利なデコレータにするために、おそらくさらに一歩進んでください:
def sequify(type_arg=1):
def wrapper(func):
def wrapped(*args, **kwargs):
it = func(f, seq)
try:
len(args[type_arg])
except:
return it
else:
return type(seq)(it)
return wrapped
return wrapper
だからあなたはこれを行うことができます:
@sequify(3)
def my_silly_function(pred, defval, extrastuff, main_iterable, other_iterable):