[英]list comprehension with generator
我用generator來生成一些列表。 但是,它不像我預期的那樣工作。 我首先使用numgen1生成列表,但它無法正常工作。 然后我切換到numgen2,這可以給我正確的想法。 但是numgen1和numgen2基本相同(至少我認為),為什么它們表現得如此不同? 願任何人給我一些解釋嗎?
def numgen1(start, end, delta):
curr=start
while curr[1] < end[1] or curr[2]<end[2]:
yield curr
curr[2] += delta
print 'Output1: ', [ i for i in numgen1([1,1,1],[1,1,5],1)]
def numgen2(start, end, delta):
curr=start
while curr[1] < end[1] or curr[2]<end[2]:
yield [curr[0], curr[1], curr[2]]
curr[2] += delta
print 'Output2: ', [ i for i in numgen2([1,1,1],[1,1,5],1)]
這是輸出。
Output1: [[1, 1, 5], [1, 1, 5], [1, 1, 5], [1, 1, 5]]
Output2: [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]
跟進問題:
在閱讀unutbu的答案之后,我又寫了一個用於測試unutbu所說的生成器。 但是發電機不像unutbu那樣表現。 我對生成器是否產生指針或值的副本感到困惑。
def numgen3(start, end, delta):
curr=start
while curr<end:
yield curr
curr += delta
print list(numgen3(1,10,1))
這是輸出。 [1,2,3,4,5,6,7,8,9]
這次我嘗試生成一些數字而不是一些列表。 但為什么不是列表9中的所有元素? 我沒有創建一個新的數字,我只是產生相同的數字(curr)。 我期望numgen3的結果應該類似於numgen1的結果。
curr
是一個清單。 curr[2] += delta
正在就地修改列表。
當你產生curr
,你會一遍又一遍地產生相同的列表。 當您打印Output1
您會看到同一列表被多次打印。
當您產生[curr[0], curr[1], curr[2]]
您將生成一個新列表。 因此,當您打印Output2
,您會看到不同的值。
注意修改curr
如何影響result
所有項目 ,因為result
是一個包含3個項目的列表,每個項目都是相同的列表curr
:
curr = [0,0,0]
result = [curr for i in range(3)]
print(result)
# [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
curr[2] = 100
print(result)
# [[0, 0, 100], [0, 0, 100], [0, 0, 100]]
您可以通過產生list(curr)
來“修復” numgen1
,因為list(curr)
返回一個新的列表,其中包含與curr
相同的元素(即“淺拷貝”):
def numgen1(start, end, delta):
curr=start
while curr[1] < end[1] or curr[2]<end[2]:
yield list(curr)
curr[2] += delta
print 'Output1: ', [ i for i in numgen1([1,1,1],[1,1,5],1)]
產量
Output1: [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]
關於numgen3
:
def numgen3(start, end, delta):
curr=start
while curr<end:
yield curr
curr += delta
print list(numgen3(1,10,1))
它有助於在可變值和不可變值之間進行精神區分。 列表是可變的,諸如ints
類的數字是不可變的。
列表是容器。 您可以在不更改對容器的引用的情況下改變其內容。 因此curr
是一個列表, curr[2] += delta
改變索引2處的內容 ,但yield curr
產生相同的列表。
在numgen3
, curr
是一個不可變的int
。 curr += delta
將curr
分配給新的immutable int
。 它不再引用同一個對象。 yield curr
產生該值。 這些不同的值在列表推導中累積,因此您可以看到包含不同值的結果。
下面是什么意思修改就地列表另一個角度來看:修改在原地做,如果列表的內容發生變化,而列表中的內存地址本身並沒有改變。
id(obj)
返回對象obj
的內存地址。 請注意,修改curr[2]
不會更改curr
的id
:
In [162]: curr = [0,0,0]
In [163]: id(curr)
Out[163]: 196192940
In [164]: curr[2] += 1
In [165]: curr
Out[165]: [0, 0, 1]
In [166]: id(curr)
Out[166]: 196192940
將其與增加分配給int
的變量時發生的情況進行比較:
In [191]: curr = 1
In [192]: id(curr)
Out[192]: 150597808
In [193]: curr += 1
In [194]: id(curr)
Out[194]: 150597796
這里, curr
沒有就地修改。 curr
被簡單地重定向以在新的內存地址處引用新值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.