range()やzip()
のような関数がforループの中でどのように使用できるかは理解しました。しかし、私は range()
がリストを出力すると思っていました - UNIXシェルの seq
のように。次のようなコードを実行すると
a=range(10)
print(a)
出力は range(10)
であり、これはリストではなく別のタイプのオブジェクトであることを示唆しています。zip()`は出力されたときに同様の動作をし、次のようなものを出力する。
<zip object at "hexadecimal number">
そこで質問なのですが、これらは何なのか、これを作ることでどんな利点があるのか、また、ループをかけずにその出力をリストにする方法はあるのでしょうか?
Python 3を使用している必要があります。
Python 2 では、zip
と range
というオブジェクトは、あなたが示唆するように、リストを返すという動作をしていました。 これらは generator-like オブジェクトに変更され、リスト全体をメモリに展開するのではなく、要求に応じて要素を生成するようになりました。 その利点は、典型的な使用例(例えば、繰り返し処理)において、より効率的であることです。
Python2.xにもlazy"バージョンは存在しますが、xrange
とitertools.izip
という異なる名前になっています。
すべての出力を使い慣れたリストオブジェクトに一度に取り出すには、単に list
を呼び出して繰り返し、内容を消費することができます。
>>> list(range(3))
[0, 1, 2]
>>> list(zip(range(3), 'abc'))
[(0, 'a'), (1, 'b'), (2, 'c')]
Python 3.x. では、range
は Python 2.x. のようにリストの代わりに range オブジェクトを返します。同様に zip
はリストの代わりに zip オブジェクトを返すようになりました。
これらのオブジェクトをリストとして取得するには、list
に入れてください。
>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> zip('abc', 'abc')
<zip object at 0x01DB7120>
>>> list(zip('abc', 'abc'))
[('a', 'a'), ('b', 'b'), ('c', 'c')]
>>>
最初は役に立たないように見えるかもしれませんが、この range
と zip
の動作の変更は、実際には効率を上げることになります。 なぜなら、zip と range オブジェクトは、一度にすべてのアイテムを保持するリストを作成するのではなく、必要なときにアイテムを生成するからです。 そうすることで、メモリの消費を節約し、動作速度を向上させることができます。
Range (python 2.* では xrange
) オブジェクトは 不変シーケンス であり、ZIP (itertools.izip
) は ジェネレータオブジェクト です。ジェネレータやシーケンスからリストを作るには、単純にlistにキャストします。例えば
>>> x = range(10)
>>> list(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
しかし、両者は要素の生成方法において異なります。通常のジェネレーターはミュータブルで、反復されると内容が消えてしまいますが、レンジはイミュータブルで、そのようなことはありません。
>>> list(x) # x is a range-object
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # second cast to list, contents is the same
>>> y = zip('abc', 'abc')
>>> list(y) # y is a generator
[('a', 'a'), ('b', 'b'), ('c', 'c')]
>>> list(y)
[] # second cast to list, content is empty!