【问题标题】:Class function acting on multiple instances of another class [duplicate]作用于另一个类的多个实例的类函数[重复]
【发布时间】:2019-09-03 01:40:57
【问题描述】:

我正在使用类构建一个简单的纸牌游戏,但遇到了一些令人困惑的事情。

首先是我的“Deck”类的相关方法。

class Deck(object):
    def __init__(self, starting_cards=[]):
        self._cards = starting_cards

    def get_amount(self):
        return len(self._cards)

    def add_card(self, card):
        self._cards.append(card)

这是我的“玩家”课程的一部分:

class Player(object):
    def __init__(self, name):
        self._name = name
        self._hand = Deck()
        self._coders = Deck()

    def get_hand(self):
        return self._hand

    def get_coders(self):
        return self._coders

    def has_won(self):
        if self._coders.get_amount() >= 4:
            return True
        else:
            return False

有一些“卡片”子类,但它们与这个问题无关,下面的代码应该向 self._hand 添加 3 张卡片,但它会将卡片添加到 self._hand 和 self._coders,它们是Deck() 类的两个完全不同的实例。

player = Player("Lochie Deakin-Sharpe")
player.get_hand().add_card(NumberCard(3))
player.get_hand().add_card(KeyboardKidnapperCard())
player.get_hand().add_card(AllNighterCard())
player.get_hand() 

我的代码的哪一部分将这些卡片添加到 self._coders,据我所知没有调用?

运行上面的代码后,这里有一些命令:

>>> player.get_coders()
Deck(NumberCard(3), KeyboardKidnapperCard(), AllNighterCard())

>>> player.get_hand().add_card(4)
>>> player.get_hand()
Deck(NumberCard(3), KeyboardKidnapperCard(), AllNighterCard(), 4)

>>> player.get_coders()
Deck(NumberCard(3), KeyboardKidnapperCard(), AllNighterCard(), 4)

【问题讨论】:

  • 有趣的阅读,我似乎无法链接到我的问题,因为我没有在我的任何代码中调用 self._coders 实例。
  • 它准确地回答了您的问题 - 您的所有 Deck 实例都使用相同的列表对象。但是看看你当前的设置,为什么要让它成为一个 arg 呢?只需在类构造函数中创建一个空列表即可。
  • 您的所有Deck 对象都有默认的list 对象。他们都使用同一个列表

标签: python python-3.x class oop


【解决方案1】:

看起来你的 Player 类有两个对同一个 _cards 列表的引用。

在 Python 中,您必须非常小心地将列表作为默认参数传递。要了解我的意思,请在定义类后运行以下代码:

p = Player('Me')
print('Memory Address of Hand: 0x{}'.format(id(p.get_hand()._cards)))
print('Memory Address of Coders: 0x{}'.format(id(p.get_coders()._cards)))

注意hand list和coder list的内存地址是一样的。这是因为将默认列表从您的参数直接传递到您的类属性的棘手行为。

为避免这种行为,请将以下代码添加到您的 Deck init 函数中:

def __init__(self, starting_cards=None):
    if starting_cards is None:
        starting_cards = []
    self._cards = starting_cards

我们将把starting_cards 设置为None,而不是直接从您的默认参数传递空列表。如果我们发现未传递起始卡片列表,则实例化一个空列表,然后将其分配给 _cards 属性。这样做可以避免对同一个数组指针的多次引用。

这是一个常见的 Python 问题。如果你有兴趣进一步了解它,这篇文章很好地触及了这个概念:https://docs.python-guide.org/writing/gotchas/

此外,Raymond Hettinger 就 Python 类开发的细微差别做了精彩的演讲。该视频来自 2013 年,但它仍然涵盖了在 Python 3 中制作 Pythonic 类所需的许多概念:https://www.youtube.com/watch?v=HTLu2DFOdTg

例如,最好在 Python 类中使用 @property 而不是显式的 getter 和 setter,这有助于随着时间的推移提高类的性能和可读性。

【讨论】:

  • 解释清楚谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-23
  • 2022-07-08
相关资源
最近更新 更多