昨天,我决定第一次开始使用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
已经改变。这是因为list1
和list2
指向同一个对象。您可以使用is
或id
在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 == list2
True
但list1 is list2
False
。它们是单独的对象。
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()
(使其成为一个新的空列表),您应该得到您所期望的。