Python 中的二维列表存在问题



昨天,我决定第一次开始使用Python,所以请放轻松,我几乎没有编程经验。 作为我的第一个小项目,我正在尝试制作一个脚本,从洗牌的牌组中洗一副牌并发5张牌。 我正在尝试将手存储在二维数组/矩阵中。 这是我遇到问题的代码:

import random
sorted_deck =  ["AH","KH","QH","JH","TH","9H","8H","7H","6H","5H","4H","3H","2H",
"AS","KS","QS","JS","TS","9S","8S","7S","6S","5S","4S","3S","2S",
"AD","KD","QD","JD","TD","9D","8D","7D","6D","5D","4D","3D","2D",
"AC","KC","QC","JC","TC","9C","8C","7C","6C","5C","4C","3C","2C"]
def shuffle():
sorted_deck_current = list()
sorted_deck_current.clear()
sorted_deck_current = sorted_deck.copy()
shuffled_deck = list()
shuffled_deck.clear()
for n in range(0,52):
r = random.randrange( 0, len(sorted_deck_current) )
shuffled_deck.append( sorted_deck_current[r] )
sorted_deck_current.pop( r )
return shuffled_deck
for n in range(2): # Check that the shuffle()-function works
fresh_shuffled = shuffle()
print( "Shuffled deck: " + str(fresh_shuffled) )
many_hands = list()
temp_hand = list()
temp_deck = list()
many_hands.clear()
for n in range(5): # Draw 5 hands from freshly shuffled decks
temp_hand.clear()
temp_deck.clear()
temp_deck = shuffle()
for i in range(5):
temp_hand.append( temp_deck[i] )
many_hands.append( temp_hand )
print( many_hands[n] )
for q in range(5): # Try and output the 5 different hands again
print( many_hands[q] )
exit()

我在代码末尾的for n in range(5)循环输出 5 个不同的手,这就是我想要的。for q in range(5)循环输出同一只手牌 5 次,这恰好是前一循环的最后一只手牌。 我不知道为什么会这样。 如果有人能向我解释这种行为,将不胜感激。

您在开始第一个for循环之前正在创建temp_hand。此后,您总是最终修改同一对象,然后将其重新添加到many_hands。实质上,您只是添加了五次相同的对象。如果你熟悉 C,它就像通过引用传递:python 在附加列表之前不会复制列表的值,它只是将引用附加到列表中。

所以后来,当你尝试打印many_hands时,它会打印同一个对象 5 次。

解决方案是在for循环内部而不是外部初始化temp_hand,以便每次迭代都会重新初始化(到新引用)。也就是说,替换行

temp_hand.clear()

它修改您当前拥有的单个列表,而不更改其引用,使用

temp_hand = list()

这会在新引用处创建一个新的空列表并将其分配给temp_hand.


顺便说一句,在初始化空列表时,通常首选使用列表文本(即[])而不是list()构造函数。这主要是因为它从周围的代码中脱颖而出,因为它绝对是一个列表,而不是碰巧返回一个列表的函数调用。

首先修复,在每次迭代时创建一个新的列表对象:

for draw in range(5): # try avoid single letter variables
temp_hand = [] # Make a NEW list object. BTW, [] is nicer and more common syntax than list()
temp_deck = shuffle()
temp_hand = temp_deck[:5] # Use slicing to get the first 5 elements, not a loop!
many_hands.append(temp_hand) # This will get a different list object appended each time which is what you want.

那么出了什么问题呢?在python中,当你将列表分配给变量时,你不是分配列表的副本,而是分配对它的引用。试试这个代码:

list1 = [1, 2, 3] # Make a new list object
list2 = list1 # Assigns a reference to the existing list1
list1[0] = 9
print(list2)

你会看到list2已经改变。这是因为list1list2指向同一个对象。您可以使用isid在python中检查这一点:

print(f"list1 id: {id(list1)}")
print(f"list2 id: {id(list2)}")

它们是一样的。还list1 is list2返回True

但是现在试试这个:

list1 = [1, 2, 3]
list2 = [1, 2, 3]

现在list1 == list2Truelist1 is list2False。它们是单独的对象。

list1 = [1, 2, 3]
list2 = [1, 2, 3]
list1[0] = 9
print(list2)

这一次list2没有改变。

在原始代码中,您通过调用.append()而不是在每次迭代中创建新的列表对象来保持对temp_hand的引用处于活动状态。所以在每个循环中,你洗牌temp_hand并打印出来,看到新的手。但是您没有看到的是many_hands中已有的值发生了什么。它们都指向同一个对象,因此它们都在每次迭代中更改。

这是证明这一点的另一种方法:

list1 = []
for _ in range(5):
list1.clear()
list1.append(1) # It is really your use of append that is the culprit as this is where you EDIT an existing list instead of ASSIGNING a new one
list2 = [1]
print(f"list1 id: {id(list1)}, list2 id: {id(list2)}")

看看list1如何总是相同的,但每次list2都会发生变化?


顺便说一句,我的代码一开始可能会更短,但我想说明如何使用[]创建一个列表,但实际上shuffle自己创建一个新列表,所以你可以这样做:

for draw in range(5): # try avoid single letter variables
temp_hand = shuffle()[:5] 
many_hands.append(temp_hand)

不是为每手牌创建一个新列表,而是为每手牌清空并填充temp_hand,因此many_hands是同一列表/手牌的 5 个引用的列表。

如果您没有temp_hand.clear()temp_hand = list()(使其成为一个新的空列表),您应该得到您所期望的。

最新更新