首页 > 解决方案 > 是否可以从一个列表中复制一个项目并多次放入另一个列表中,然后单独修改每个项目?

问题描述

我正在开发一个与《炉石传说》的战场模式类似概念的纸牌游戏项目(用于练习和娱乐)。我试图将两张或多张同一张牌放在玩家手中,然后单独修改其值。目前,当一张卡应该被修改时,它会同时改变。

首先,我引入怪物 json 并将其深度复制到一个列表中,以便我可以 1)修改条目和 2)保留原始值的引用:

gameDeck = []
with open('monsters.json', 'r') as f:
    monsters = json.load(f)
gameDeck = copy.deepcopy(monsters)

怪物条目的示例(它是列表中的字典):

[
  { "name": "Jerry",
    "hp": 1,
    "str": 1,
    "type": "human",
    "amount": 10,
    "ability": "",
    "ability-property":"",
    "ability-property-amount":""
  },

然后我将卡片添加到用户手中(强制,用于测试):

def forceAssignUser(user, gameDeck):
    user.addCard(card, gameDeck)
    user.addCard(card, gameDeck)

.addCard 看起来像:

class Player:
    #...

    # adds a card to the players hand, removes the card from the deck
    def addCard(self, card, gameDeck):
        self.hand.append(card)
        # gameDeck.remove(card)
        card['amount'] -= 1

当然,现在在奔跑和“战斗”阶段,当一张牌受到伤害时,它也会被复制。我试过了,但结果是一样的:

def forceAssignUser(user, gameDeck):
    card = copy.deepcopy(gameDeck[0])
    user.addCard(card, gameDeck)
    user.addCard(card, gameDeck)

这是“战斗”阶段的一部分,大部分变化都发生在这里(不漂亮,我知道):

    while userAlive is not False and opponentAlive is not False:
        if whoseTurn == "u":
            if userMonAtk >= len(user.hand):
                #what monster will attack
                userMonAtk = 0
            while checkCardHealth(user, userMonAtk) <= 0:
                #use next monster if current is dead
                userMonAtk += 1
            #choose a random target
            randOpponentMon = random.randint(0, len(user.hand) - 1)
            while opponent.hand[randOpponentMon]['hp'] <= 0:
                #reroll if chosen enemy dead
                randOpponentMon = random.randint(0, len(user.hand) - 1)
            usMonster = user.hand[userMonAtk] #store the card info
            opMonster = opponent.hand[randOpponentMon]
            opMonster['hp'] -= usMonster['str'] #when attacking or being attacked, take damage
            usMonster['hp'] -= opMonster['str']
            printBoard(user, opponent)
            print("Your {0} attacks opponent's {1}!".format(usMonster['name'], opMonster['name']))
            opponentAlive = checkDeckHealth(opponent)
            userAlive = checkDeckHealth(user)
            input("Press anything to continue.")
            whoseTurn = "o"
            userMonAtk += 1

甚至有可能做我想做的事吗?我不擅长编码,所以我的很多代码都围绕着逻辑、方法和类。

标签: pythonlistduplicates

解决方案


假设你有几个对象(Python 的object内置是有用的替代品,因为它 repr 包括它的地址):

>>> o1,o2=object(), object()
>>> o1
<object object at 0x10817e430>
>>> o2
<object object at 0x10817e450>

现在列出这些对象:

>>> li=[o1,o2]
>>> li
[<object object at 0x10817e430>, <object object at 0x10817e450>]

一个典型的副本会使用一个切片来做一个浅拷贝:

>>> li_copy=li[:]
>>> li_copy
[<object object at 0x10817e430>, <object object at 0x10817e450>]

这意味着lili_copy是两个不同的列表;您可以修改一个而不修改另一个:

>>> li_copy[0]='not an object() anymore'
>>> li
[<object object at 0x10817e430>, <object object at 0x10817e450>]
>>> li_copy
['not an object() anymore', <object object at 0x10817e450>]

尽管它们是两个独立的列表,但它们确实包含相同的对象;如果你修改<object object at 0x10817e430>它也会改变li

但是如果你使用deepcopy你会得到两个带有两个独立对象的单独列表:

>>> import copy
>>> li_3=copy.deepcopy(li)
>>> li_3
[<object object at 0x10817e4b0>, <object object at 0x10817e4c0>]
# note that the addresses have changed - these are new objects that could
# be modified without changing the object in the other list.

并非所有对象都支持深度复制;有时 deepcopy 函数会简单地做与切片复制相同的事情。

Deepcopy 需要通过定义__deepcopy__()它必须支持创建具有第一个实例的默认值但又是一个新实例的新对象的功能来定义。它object()只是一个哑对象,它只是object()再次调用。

因此,在您的情况下执行此操作的方法是定义一个Card对象并定义一个__deepcopy__方法,该方法使用复制源的值创建一张新卡。


推荐阅读