Python 3でmapがリストではなくmapオブジェクトを返すのはなぜですか?


82

Python3.xの新しい言語設計を理解することに興味があります。

Python 2.7では、次の関数を楽しんでいますmap

Python 2.7.12
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: [2, 3, 4]

ただし、Python 3.xでは、状況が変更されました。

Python 3.5.1
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: <map at 0x4218390>

方法はわかりましたが、理由の説明が見つかりませんでした。言語設計者がこの選択をしたのはなぜですか。私の意見では、これは非常に苦痛をもたらします。これは、リスト内包表記に固執することで開発者をアームレスリングするためでしたか?

IMO、リストは当然ファンクターと考えることができます; そして私はどういうわけかこのように考えると考えられてきました:

fmap :: (a -> b) -> f a -> f b

2
理論的根拠は、リスト内包表記の代わりにジェネレーターを使用する理由と同じである必要があります。遅延評価を使用することで、巨大なものをメモリに保持する必要がなくなります。ここで受け入れられた答えを確認してください:stackoverflow.com/questions/1303347/…–
Moberg

8
これが「大きな苦痛」をもたらす理由を説明していただけますか?
RemcoGerlich 2016年

3
何年にもmapわたる使用法が、最も一般的な使用法が結果を単純に繰り返したためだと思います。必要のないときにリストを作成するのは非効率的であるため、開発者はmap怠惰にすることにしました。パフォーマンスのためにここで得られるものはたくさんあり、失われるものはたくさんありません(リストが必要な場合は、リストを要求してください... list(map(...)))。
mgilson 2016年

3
わかりました。Functorパターンを維持してListの遅延バージョンを提供するのではなく、マップされるたびにリストの遅延評価を強制することをどういうわけか決定したのは興味深いことです。私は自分で選択する権利を持っていることを望んでいました、別名、ジェネレーター->マップ->ジェネレーターまたはリスト->マップ->リスト(決定するのは私
次第

4
@NoIdeaHowToFixThis、実際にはあなた次第です。リスト全体が必要な場合は、リストに変換するだけです。簡単です
Netwave 2016年

回答:


37

私はまだ存在してマッピングした理由だと思うすべてのときジェネレータ式は:も存在するが、それはすべての上にループや関数に渡され、複数のイテレータの引数を取ることができるということです

>>> list(map(min, [1,2,3,4], [0,10,0,10]))
[0,2,0,4]

これは、zipを使用するよりも少し簡単です。

>>> list(min(x, y) for x, y in zip([1,2,3,4], [0,10,0,10]))

それ以外の場合は、ジェネレータ式に何も追加されません。


1
リスト内包表記がよりパイソン的であり、言語設計者がそれを強調したかったことを強調したいという願望を追加すると、これが最もオンスポットの答えだと思います。@vishes_shellはどういうわけか言語設計に十分に焦点を合わせていません。
noIdeaHowToFixThis 2016年

2
2つのリストの長さが等しくない場合、 Python2と3で異なる結果が生成されます。お試しc = list(map(max, [1,2,3,4], [0,10,0,10, 99]))のpython 2とPython 3に
cdarke

1
python3からマップを完全に削除する元の計画のリファレンスは次のとおりです。artima.com
Bernhard

マップをリストでラップすると、1つの要素リストのリストが表示されます。
awiebe 2017

24

イテレータを返すため、フルサイズのリストをメモリに保存することを省略します。将来、メモリに負担をかけずに簡単に繰り返すことができるようにします。おそらく、完全なリストは必要ありませんが、条件が満たされるまではその一部が必要です。

このドキュメントは便利です。イテレータは素晴らしいです。

データのストリームを表すオブジェクト。イテレータの__next__()メソッドを繰り返し呼び出す(または組み込み関数に渡すnext())と、ストリーム内の連続するアイテムが返されます。利用可能なデータがなくなると、StopIteration代わりに例外が発生します。この時点で、イテレータオブジェクトは使い果たされ、その__next__()メソッドをさらに呼び出すと、StopIteration再び発生します。イテレータには__iter__()、イテレータオブジェクト自体を返すメソッドが必要です。これにより、すべてのイテレータもイテレータであり、他のイテレータが受け入れられるほとんどの場所で使用できます。注目すべき例外の1つは、複数の反復パスを試行するコードです。コンテナオブジェクト(などlist)は、コンテナオブジェクトをに渡すたびに新しいイテレータを生成します。iter()関数またはforループで使用します。イテレータでこれを試行すると、前の反復パスで使用されたものと同じ使い果たされたイテレータオブジェクトが返され、空のコンテナのように見えます。


14

Guidoはここでこの質問に答えます:「リストを作成するのは無駄だからです」。

彼はまた、正しい変換は通常のforループを使用することであると言います。

map()2から3への変換は、単にそのlist( )周りに固執するという単純なケースではないかもしれません。Guidoも言います:

入力配列は同じ長さではない場合」、map()配列の最短の終了時に停止する。との完全な互換性のためmap()のPython 2.xから、また中の配列ラップitertools.zip_longest()例えば、

map(func, *sequences)

になります

list(map(func, itertools.zip_longest(*sequences)))

「」


3
Guidoコメントは、ファンクターとしての使用ではなくmap()、関数の副作用のため呼び出されます。
abukaj 2016年

4
での変換zip_longestは間違っています。itertools.starmap同等になるには、を使用する必要がありますlist(starmap(func, zip_longest(*sequences)))。これzip_longestは、タプルを生成するためです。したがって、は、を呼び出す場合のように個別の引数ではなくfunc、単一のnuple引数を受け取ります。nmap(func, *sequences)
バクリウ2016年

12

Python 3では、多くの関数(だけでmapなくziprangeなど)が完全なリストではなくイテレーターを返します。イテレータが必要な場合(たとえば、リスト全体をメモリに保持しないようにするため)、またはリストが必要な場合(たとえば、インデックスを作成できるようにするため)。

ただし、Python 3での変更の主な理由は、イテレータをlist(some_iterator)逆の同等物を使用してリストに変換するのは簡単iter(some_list)ですが、完全なリストがすでに作成されてメモリに保持されているため、望ましい結果が得られないためだと思います。

たとえば、Python 3ではlist(range(n))rangeオブジェクトを作成してリストに変換するコストがほとんどないため、問題なく機能します。ただし、Python 2iter(range(n))ではrange()、イテレータが構築される前に完全なリストが構築されるため、メモリは保存されません。

したがって、Python 2では、リストではなくイテレータを作成するために、imapfor map完全に同等ではありませんが)、xrangefor rangeizipforなどの個別の関数が必要ですzip。対照的に、Python 3では、list()必要に応じて呼び出しによって完全なリストが作成されるため、単一の関数が必要です。


Python 2.7のAFAIKは、itertoolsリターンイテレータからも機能します。また、リストは複数回繰り返され、ランダムにアクセスされる可能性があるため、イテレーターを遅延リストとは見なしません。
abukaj 2016年

@abukajわかりました、わかりやすくするために回答を編集しました
Chris_Rands 2016年

@IgorRivinどういう意味ですか?Python3mapオブジェクトにはnext()メソッドがあります。Python 3のrange範囲オブジェクトは厳密には私が知っているイテレータではありません
Chris_Rands 2017年

私のAnacondaディストリビューションpython3.6.2の@Chris_Randsfoo = map(lambda x: x, [1, 2, 3])は、マップオブジェクトを 返しますfoo。実行foo.next()するとエラーが発生します:'map' object has no attribute 'next'
Igor Rivin 2017年

1
@IgorRivin:で始まるメソッドと終わるメソッド__はPythonに予約されています。その予約がないと、next単なるメソッドであるもの(実際にはイテレータではない)とイテレータであるものを区別するのに問題があります。実際には、メソッドをスキップして、2.6以降のすべてのPythonバージョンで正しくnext()機能する関数(例next(foo))を使用する必要があります。うまく機能する場合len(foo)でも、使用するのと同じ方法ですfoo.__len__()。dunderメソッドは通常、直接呼び出されるのではなく、他の操作の一部として暗黙的に呼び出されることを目的としてます。
ShadowRanger 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.