【问题标题】:Python question about functions classes and class methods关于函数类和类方法的Python问题
【发布时间】:2021-06-30 22:34:41
【问题描述】:

我对中级编程相当陌生,但已经玩过代码一段时间了。 目前我正在制作一个简单的纸牌游戏。 这个问题是我不确定在哪里使用我的函数或何时将它们设为类方法。

例如,这里有一个简单的函数,它向每个玩家发 5 张牌(从预定义的列表中),然后从牌组顶部翻出一张牌(实际上都是随机选择)。 卡片作为项目列表(3 个列表)返回。 我还创建了一个名为“Player”的类。

p1_hand = []
p2_hand = []
flip_card = []

def deal_deck():
    count = 0
    for i in range(0, 10, 1):
        count += 1
        if count % 2 == 0:
            card = random.choice(deck)
            p2_hand.append(card)
            deck.remove(card)
        else:
            card = random.choice(deck)
            p1_hand.append(card)
            deck.remove(card)
        if count == 10:
            flip_card.append(random.choice(deck))
    return p2_hand, p1_hand, flip_card

在这个例子中,它只是一个交易,所以我想知道为什么它需要是“Player”的一个类方法? 可能“Player”类除了记分、跟踪谁在发牌以及记分是多少之外根本没有做太多的事情?

简而言之,我无法将类理解为交互和执行动作的对象,我已经创建了其他类,它们最终像迷你数据库一样工作,而不是在方法中使用太多复杂性。

【问题讨论】:

  • 我的直觉是创建一个名为 Game、Dealer、Deck、Card、Player 和 Hand 的类。 Dealer 类将有一个 Deck 数据成员。 Deck 会有一个 shuffle 方法。 Player 类将有一个 Hand 数据成员。 deal_deck 将成为 Dealer 类方法,并将 Card 对象从 Dealer's Deck 传输到每个 Player's Hand,然后传输到 Game 对象的 Flipped_Card 成员。
  • 这可能是主观的;但对我来说,发牌是Deck 实例所做的事情,可能是通过告诉Player(或者更好,Hand)实例从自己身上抽一张牌。如果你有一种奇怪的交易,我会把它放在Game。具体来说,如果这是在游戏开始时发生的事情,我会让Game.__init__ 初始化一个Deck 实例,然后告诉它发牌,然后告诉它显示最上面的牌。我不会像Player 类方法那样处理:正如你所指出的,deaing 与抽象的“玩家性”并没有真正的关系,尤其是与翻牌无关。

标签: python function class methods scope


【解决方案1】:

设计类和对象是一门艺术。使用类的根本目的是信息隐藏。程序的其余部分不必知道一副纸牌是如何实现的,这样您就可以在不重新设计整个程序的情况下更改实现。因此,您创建了一个Deck 类,该类将所有数据都存储在内部,并且只向外界公开您想要使用卡片组执行的操作,例如shuffledeal_cardPlayer 类可能包括 handscore,以及添加另一张牌的功能,Game 对象(也许)可以协调将牌发到手中并触发游戏。

您拥有的代码混合了所有这些。它必须知道一副牌是如何实现的,一手牌是如何实现的,以及一张牌是如何翻转的。

顺便说一句,为了真实起见,你最好洗牌并从顶部发牌,而不是使用random.choice

【讨论】:

  • 是的,谢谢,我发现“shuffle”是一个更好的函数,可以在我的代码中使用。
【解决方案2】:

我最初认为这太宽泛了,但在为您写笔记时改变了主意。类是一种编程工具,其实现在您所要求的级别上没有得到太多处理。互联网上有很多优秀的纸牌游戏课程的例子……也有很多糟糕的。这项研究对你来说并不容易。

使用一个类来表示系统的一个单元(在本例中为纸牌游戏),该单元具有内聚性(一组数据和功能具有易于理解的边界)并与其他单元或与主程序交互。

在这种情况下,您有一个良好的开端:您已将 cardplayerhand 识别为游戏系统中的实体。您可能希望将套牌视为hand 实例(只是另一个卡片列表),或者由于游戏中的不同功能,您可能希望对其进行特殊处理。

我认为有用的类和函数包括:

Deck
    The impartial source of cards
data
    a list of cards
methods
    reset -- shuffle all 52 cards into a list
    deal(n) -- return a list of n cards

Hand
    cards held by a single player
data
    a list of cards
methods
    reset -- whatever is needed to return the hand to a game-start state
    draw(n) -- acquire n cards
    play(n) -- play n cards to the game area
    
Card
    A single card, containing all information needed to identify it to the game
data
    suit
    rank
methods
    none

Player
    Game information about each player
data
    hand -- see Hand class above
    game-role -- depending on the game, this could be "dealer", "idle", "active", ...
    ... other, depending on the game: points, money, etc.
methods
    ... very dependent on the game being played

Game
    the overall monitor for the game
data
    roster -- list of players
    deck -- see Deck class above
    ... other, depending on the game: round, player to play next, etc.

其中一些有些重叠,例如“Deck.deal”和“Hand.draw”。 您面临的设计决策之一是选择哪个实体将驱动两个对象之间的交互。

至于实现,我建议您将基本操作简化很多。 对于桌子,通过生成所有 52 张带有嵌套列表理解的卡片进行初始化。 shuffle甲板,然后使用列表切片来处理卡片。例如,给每个玩家发 5 张牌:

def deal(n):
    take = deck[:n]
    deck = deck[n:]
    return take

# elsewhere, to drive the initial dealing ...
for player in roster:
    player.hand = deck.deal(5)

简单地减少交互以匹配您谈论游戏的方式:“给每个玩家发五张牌”应该看起来就像代码中的那样。埋葬每个类中的机制。

【讨论】:

  • 这是什么编码风格?您是否有任何建议的阅读或主题/链接可以帮助我像这样简化我的代码?
  • 这是面向对象的设计——一种“风格”,你应该已经有了材料,因为你正在使用类。大部分简化只是学习语言,而不是“风格”。大多数情况下,它采用简单的模块化设计:决定每个函数或块将执行什么简单任务 - 然后直接执行并返回调用程序。
  • 感谢非常有帮助,我刚刚进入它的这一方面。
猜你喜欢
  • 1970-01-01
  • 2020-01-30
  • 2011-05-05
  • 1970-01-01
  • 1970-01-01
  • 2010-10-08
  • 2019-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多