我的目标是通过设计和使用类并从继承和其他OOP概念入手,使自己对OOP有所了解。

我写了一个很小的代码来玩名为“战争”的纸牌游戏。规则很简单。每个玩游戏的人都会得到一张卡。卡数最高的人获胜。我现在不必为Ties担心太多。我的代码可以工作,但是我希望获得有关我的OOP使用情况的反馈。


import itertools
import random
class Cards:
    def __init__(self):
        self.suits = ['Spades','Hearts','Diamonds','Clubs']
        self.values = range(1,14)
        self.ActualCards = [] #Empty List to Append
        for Card in itertools.product(self.suits,self.values):
            self.ActualCards.append(Card) #Cartesian Product to Create Deck
    def GetRandomCard(self):
        RandomNumber = random.randint(0,51)
        CardToBeReturned = self.ActualCards[RandomNumber] #Generates Random Card
    return(CardToBeReturned)

class Player:
    def __init__(self,ID,Card):
        self.PlayerID = ID
        self.CardForPlayer = Card

class Game:
    def __init__(self,NameOfGame):
        self.name = NameOfGame

class SimpleWar(Game):
    def __init__(self,NumberOfPlayers):
        self.NumberOfPlayers = NumberOfPlayers
        self.PlayerList = []
    def StartGame(self):
        DeckOfCards = Cards()
        for playerID in range(0,self.NumberOfPlayers):
            CardForPlayer = DeckOfCards.GetRandomCard() #Deal Card to Player
            NewPlayer = Player(playerID,CardForPlayer) #Save Player ID and Card
            self.PlayerList.append(NewPlayer)
        self.DecideWinner()    
    def DecideWinner(self):
        WinningID = self.PlayerList[0] #Choose Player 0 as Potential Winner
        for playerID in self.PlayerList:
            if(playerID.CardForPlayer[1]>WinningID.CardForPlayer[1]):
                WinningID = playerID #Find the Player with Highest Card
        print "Winner is Player "+str(WinningID.PlayerID)
        print "Her Card was "+ str(WinningID.CardForPlayer[1]) + " of " + str(WinningID.CardForPlayer[0]) 


if __name__=="__main__":
    NewGame = SimpleWar(2)
    NewGame.StartGame()


#1 楼

语法错误

Cards.GetRandomCard()中,return语句的缩进不正确。 (另外,我建议写return时不要带括号。)

样式约定

lower_case_names用于变量和方法。

为便于阅读,请在每个逗号后面添加一个空格,并在函数定义之间添加一个换行符。

建模

您的Game类没有用。您从未设置或使用NameOfGame。我建议完全放弃Game类。

在现实的纸牌游戏中,您需要将一张纸牌从卡组中分发给每个玩家,而无需更换。在您的代码中,您需要进行替换(即,有可能向两个玩家都发出相同的卡)。更现实的仿真将在阵列上执行random.shuffle()。交易时,您可以从列表中将其从卡组中删除。 51是一个“魔术”数字;您应该使用pop()

len(self.ActualCards) - 1确实是Cards。您应该拥有一个代表单个卡的Deck类,而不只是一个字符串元组。 Card类应具有Card方法,该方法返回诸如__str__()之类的字符串。如果您还定义比较运算符,则可以使用"Ace of diamonds"来确定获胜者。

表现力

在Python中,通常可以避免创建空列表并将其追加: br />
self.ActualCards = [] #Empty List to Append
for Card in itertools.product(self.suits,self.values):
    self.ActualCards.append(Card) #Cartesian Product to Create Deck


相反,您应该能够一次全部构建它:

self.actual_cards = list(itertools.product(self.suits, self.values))


评论


\ $ \ begingroup \ $
PEP 8建议在函数和类定义之间使用两条换行符。但是,总比没有好。
\ $ \ endgroup \ $
–nyuszika7h
2014年2月19日14:44



\ $ \ begingroup \ $
@ nyuszika7h PEP 8只需要在类中的方法定义之间插入空白行,这很重要。
\ $ \ endgroup \ $
– 200_success
2014年2月19日在17:12

\ $ \ begingroup \ $
@ 200_success:很难定义纸牌的比较,因为顺序仅在特定游戏的上下文中才有意义。所以比较两张牌是游戏的责任(战争,纸牌等)。
\ $ \ endgroup \ $
– mgarciaisaia
2014-2-19在18:02

\ $ \ begingroup \ $
@ 200_success是的,我没有费心检查它是否在课程中。
\ $ \ endgroup \ $
–nyuszika7h
2014年2月20日在17:30

#2 楼

从测试的角度来看,最好将套牌(Cards)和玩家注入游戏中。我认为,在游戏对象中打印结果也不是一个好主意。返回包含获胜者的一轮比赛可能会更好。然后,这也可以用于记录日志或掌握权限:)

Deck = Deck()
Game = SimpleWar(Deck)
Game.addPlayer(Player('Andrea'))
Game.addPlayer(Player('Bert'))
Round = Game.play()
Winner = Round.get_winner()
print('The winner is: ' + str(Winner))
print('The winning card is: ' + str(Winner.get_last_card()))

Deck.shuffle()
Round = Game.play()


如前所述,卡座应包含卡片对象(或数组并构建卡片对象,根据要求,如果这些纸牌物品会很昂贵),则在每次游戏回合后都必须将纸牌放回卡组中(否则游戏可以调用卡组的reset()方法)。然后的问题是,在所有纸牌都已退回卡组之后,谁会记住获奖纸牌。在上面的示例中,这是播放器(get_last_card()),但是它也可以存储在Round中。

这样,您的类中没有任何对象实例化,除了甲板,用于构建卡片对象。这将是很好的可测试的。例如,您可以将套牌模拟传递到游戏中并定义在单个请求中应返回的卡片以测试获胜者的检测。

在网络上搜索“依赖注入”以了解更多信息信息。

评论


\ $ \ begingroup \ $
我喜欢您的设计的原因是您已经考虑了对象之间的交互,并定义了对象的方法。对于依赖项注入也有很大的好处。
\ $ \ endgroup \ $
– sea-rob
14年2月19日在19:11

#3 楼

很酷的应用程序。我的评论并不是要批评别人或傲慢无礼,只是我想了想。


Python 3?如果不是,请确保您的类扩展了object
我可能不会定义和扩展Game。它没有添加任何东西。您无需泛化或抽象Game的概念,尤其是在这种情况下。
从更广泛的意义上讲,避免继承是一种好习惯。在“偏爱组成优先于继承”上进行更多搜索。
Cards的状态不变...拥有类的意义是管理状态。所以我会有类似pick_card这样的东西,实际上是从卡组中删除了选定的卡。否则,最好只使用一个函数(至少在Python中)更好。类都是关于捆绑数据和功能的,因此您可以安全地管理状态。缺席的话,他们会失去光泽(除非您正在编写Java;)。)
这就是我,但我不会让循环或决定获胜者。再次,我将开始游戏,然后再输入类似StartGame的内容。同样,依靠类来管理状态。每个方法都应该将其进一步发展。
我知道,我将__init__run_game__init__play_a_turn分解为方法,并在类外循环以保持调用结构平坦。确定获奖者的逻辑和展示获奖者的逻辑可以采用不同的方法。我喜欢保持方法的小巧,轻便和有针对性。
再次,我希望decide_game做得更多,而show_game做得更少。如果我真的有一个SimpleWar.__init__

编辑:我喜欢将类定义为与它们建模的对象紧密相关。因此,我可能不是Deck,而是qeck,并使用洗牌和挑选方法。我也喜欢上面关于分离单独的卡比较方法的评论,我一定会这样做。

评论


\ $ \ begingroup \ $
它不是Python 3,因为它使用Python 2的打印语法。
\ $ \ endgroup \ $
–nyuszika7h
2014年2月19日14:43在

\ $ \ begingroup \ $
啊,好电话。我仍然对没有迁移感到自觉。在这种情况下,请确保您的init方法直接或通过super()调用基类的init。
\ $ \ endgroup \ $
– sea-rob
2014年2月19日下午16:00

\ $ \ begingroup \ $
为了将来参考,请使用__init__使其正确显示。或者,如果您想加粗,** \ _ \ _ init \ _ \ _ **。
\ $ \ endgroup \ $
–nyuszika7h
2014年2月20日在17:36



#4 楼

乍一看,我建议将CompareTo(OtherCard)函数放入Card类中,并将其用于DecideWinner

评论


\ $ \ begingroup \ $
卡的价值取决于游戏。在这些卡可以用于许多不同游戏的世界中,卡不知道自己的价值。因此,我认为,WarGame对象是做出此决定的好地方。
\ $ \ endgroup \ $
–stofl
2014年2月19日在15:05



\ $ \ begingroup \ $
@stofl对于数字比较,唯一会改变的是ace的高低,而实际上根本没有关系,除非ace可能在同一游戏中,或者是否在两张游戏之间传递了卡片一些理由。
\ $ \ endgroup \ $
–维特鲁威
14年2月19日在18:50

\ $ \ begingroup \ $
如果卡的值是卡的属性,则比较方法在卡类中有意义。如果在游戏规则中定义了卡的值,则卡类将不是比较方法的正确位置。在具体的代码中,我们在纸牌中具有价值-在现实世界中,我们通常不知道:纸牌在不同的游戏中具有许多不同的价值。
\ $ \ endgroup \ $
–stofl
14年2月19日在19:05

\ $ \ begingroup \ $
@stofl我认为代码中的内容比现实中的内容更重要。在代码中,只有一种游戏,只有一种卡牌比较定义。
\ $ \ endgroup \ $
–维特鲁威
2014年2月19日19:30在