【问题标题】:Problem with 2-dimensional list in PythonPython中二维列表的问题
【发布时间】:2019-07-11 22:17:41
【问题描述】:

昨天,我决定第一次开始玩 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 次,这恰好是前一个循环的最后一手牌。 我不知道为什么会这样。 如果有人可以向我解释这种行为,将不胜感激。

【问题讨论】:

  • 如果您打印many_hands 的第一个元素,您会注意到它在每次迭代中都会发生变化。所有的手都是同一个对象。
  • many_hands.append( temp_hand ) 的作用是将many_hands 附加到temp_hand引用。这意味着many_hands[0] 现在指向与temp_hand 完全相同的列表。因此,当您更改temp_hand,然后再调用many_hands[0] 时,您现在指向的是更改后的temp_hand。你这样做了 5 次。

标签: python


【解决方案1】:

在开始第一个 for 循环之前,您正在创建 temp_hand。此后,您总是会修改同一个对象,然后将其重新添加到many_hands。本质上,您只是将同一个对象添加了五次。如果你熟悉 C,这就像 pass-by-reference:python 在追加列表之前不会复制列表的值,它只是追加对列表的引用。

然后,当您尝试打印many_hands 时,它会打印同一个对象 5 次。

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

temp_hand.clear()

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

temp_hand = list()

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


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

【讨论】:

    【解决方案2】:

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

    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 指向同一个对象。您可以在 python 中使用isid 进行检查:

    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)
    

    【讨论】:

      【解决方案3】:

      您无需为每手牌创建一个新列表,而是为每手牌清空并填写 temp_hand,以便 many_hands 是对同一列表/手牌的 5 个引用的列表。

      如果你用temp_hand = list() 代替temp_hand.clear()(使它成为一个新的空列表),你应该得到你所期望的。

      【讨论】:

      • 好吧,不是份。每次都是相同的列表对象。
      猜你喜欢
      • 2020-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多