2016-10-01 4 views
3

Pythonのイテレータの概念を理解しようとしており、Python 3.5.2でこれを試してみました。イテレータオブジェクトを作成した後にリストを削除する

x = list(range(1000)) # size of x is 9112 bytes 
y = iter(x)    # size of y is 56 bytes 
del x 
x = list(y)    # size of x is again 9112 bytes 

イテレータは、生成するシーケンスに関する情報をどのように格納しますか?

すべての要素が含まれているわけではありませんが、元のリストを削除した後でも、イテレータから元のリストを再現できますか?

すべての要素が含まれていない場合、削除後も次の要素がどのようになっているかをどのように知るかx

+0

短い答え....元のシーケンスを再現するのに十分な情報がカプセル化されているので...あなたがそれを消費するとき – danidee

答えて

2

イテレータは、メモリ内に「次の要素」を持たずにシーケンスの次の要素を生成できるように、十分な詳細が格納されているためです。我々は、私たちは次を生成可能にするのに十分な詳細(開始点と私たちのイテレータの終点)に格納されてきた私たちの__init__方法で

class Fakeiterator: 
    def __init__(self, range_list): 
     self.current = range_list[0] 
     self.high = range_list[-1] 

    def __iter__(self): 
     return self 

    def __next__(self): 
     if self.current > self.high: 
      raise StopIteration 
     else: 
      self.current += 1 
      return self.current - 1 

私たち自身の偽のイテレータを作成してみましょうに何が起こっているかを理解するには

要素を実際にメモリに入れないでください。この情報がある限り、2000要素を含むリストが与えられていても、開始点と終了点を知るだけで、イテレータの次の要素を求めるときはいつでも、開始点と終了点を知ることができます。

__next__メソッド単に現在のカウンタをインクリメントして、それを私たちに返します。

>>> x = list(range(5)) 
>>> y = Fakeiterator(x) 
>>> del x 
>>> list(y) 
[0, 1, 2, 3, 4] 
>>> 

listコンストラクタを繰り返し呼び出すStopIterationまで__next__は、私たちのイテレータによって上昇し、それが現在の要素が、我々はの作成時に保存された最大の要素よりも高いポイントである:

は、私たちのイテレータをテストすることができますイテレータ

iter(x)をリストに追加した場合はSTORES x内部でlist_iteratorオブジェクトを返します。 xはまだ保存されていますが、名前はxではありません。

なぜgetsizeofは、元のリストのサイズよりも大きいか、少なくとも等しいと思われる小さいサイズを返します。ドキュメントから

sys.getsizeof(オブジェクト[、デフォルト]) バイトのオブジェクトのサイズを返します。オブジェクトには、任意のタイプのオブジェクトを使用できます。すべてのビルトインオブジェクトは が正しい結果を返しますが、これは実装固有のものとして サードパーティの拡張機能には当てはまりません。

オブジェクトに直接起因するメモリ消費量は、参照するオブジェクトのメモリ消費ではなく、 です。

オブジェクトが サイズを取得する手段を提供していない場合、デフォルトが返されます。それ以外の場合は、TypeErrorが発生します。

getsizeof()オブジェクトののsizeofメソッドを呼び出して、オブジェクトが ガベージコレクタによって管理されている場合 追加のガベージコレクタのオーバーヘッドを付加します。それはあなたがスクリプトを実行するとのクイックスクリプト

import sys 

x = [1, 2, 3] 

print(sys.getsizeof(x)) 

class storex(): 
    def __init__(self, param): 
     self.param = param 

y = storex(x) 

print(sys.getsizeof(y)) 
print(y.param, sys.getsizeof(y.param)) 

を書いてみましょう実証するために

。我々はそれが自動的に作成しませんstorexの属性として保存するとき、リスト[1, 2, 2]は、88バイト長であっても、これは(私のマシン上で、それはあなたと同じでなければなりません)が出力され

88 
56 
[1, 2, 3] 88 

storexがそれより大きくなります。 storexがそれを参照しているためです。それは直接

storexの一部ではありません。しかしy.paramのサイズを印刷するには、我々はそれがまだ元[1, 2, 3]リストと同じサイズだと見ることができます。またdelメモリからオブジェクトを削除しません

、それは単にアンバインドxという名前で、xはメモリ内のオブジェクトを参照しません。 xの値は、それへの参照が再び存在しない場合にここで

iは

>>> x = [1,2,3] 
>>> class y: pass 
... 
>>> y.x = x 
>>> id(x), id(y.x) 
(140177507371016, 140177507371016) 
>>> del x 
>>> id(y.x) 
140177507371016 
>>> x 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
NameError: name 'x' is not defined 
>>> 

xを削除すると、自動的に[1,2,3]y.xポイントを削除しない何を意味するかのデモンストレーションです(ガベージコレクション)破棄されますto、彼らの両方のIDがメモリ内の同じオブジェクトを指していることをIDで示したとしても。

+0

ありがとう、この遅い返事を申し訳ありません。しかし、私はまだ疑問が残っています: – racerX

+0

a)範囲を例として掲示しました。その場合、低い値と高い値とステップを使用して情報を保存するのは簡単です。しかし、シーケンスがランダムであればどうでしょうか? – racerX

+0

(申し訳ありませんが、自分のコメントを編集できない理由がわからず、Enterキーを押すとすべての文章が新しいコメントとして投稿され続けます)b)xを削除すると、イテレータはまだそのチャンクそれ以外の場合は値を生成することができません。したがって、メモリに関しては、Python 3.5の範囲のようなinbuilt iterablesを使用する場合にのみ役立ちます。私の例では、Pythonは内部的にその範囲を知ることができず、メモリ内にリスト全体が格納されている必要があり、元のポストで何をしたのかという概念を理解する以外に理由はありません。 – racerX

1

私が知っていることによると、あなたのyがまだそれを参照しているので、del xはメモリ内の値をdelにしません。ポインタのようなものです。 xとyは同じメモリを参照しています。

あなたがdel xを実行すると、pythonはxを参照してガベージコレクションを行います。

x = list(y)を実行すると、メモリが再びxを指しています。

関連する問題