2010-09-16 45 views
6

所以我有这个代码的对象。这个对象是你可以在岩石剪刀游戏中做出的一个举动。 现在,该对象需要既是一个整数(用于匹配协议)和一个字符串以方便编写和查看。我如何使这个代码Pythonic

class Move: 
    def __init__(self, setMove): 
     self.numToName = {0:"rock", 1:"paper",2:"scissors"} 
     self.nameToNum = dict(reversed(pairing) for pairing in self.numToName.items()) 
     if setMove in self.numToName.keys(): 
      self.mMove=setMove 
     else: 
      self.mMove=self.nameToNum.get(setMove) #make it to a number 

    def defeats(self): 
     return Move((self.mMove-1)%3) 
    def losesTo(self): 
     return Move((self.mMove+1)%3) 
    def tiesWith(self): 
     return self 

    #Operator overloading 
    def __eq__(A,B): 
     return A.mMove==B.mMove 
    def __gt__(A,B): 
     return A.defeats(B) 
    def __lt__(A,B): 
     return A.losesTo(B) 
    def __ge__(A,B): 
     return A>B or A==B 
    def __le__(A,B): 
     return A<B or A==B 

    def __str__(self): 
     return self.numToName.get(self.mMove); 

    def __int__(self): 
     return self.mMove; 

现在我有点新来python,来自C和Java背景。 python中的一件大事就是只有一种正确的方法来做某件事。 另一件事是不担心类型。 我很明确地担心这里输入。

所以我不知道什么是正确的方式来处理这些对象。 目前我有一个对象,它是任何3种类型之一(或更多,但我不知道会做什么) 也许我应该使用不同类的对象?并让他们成为单身人士? 另外我的对象目前在创建后可以修改,这在我的脑海中是一件坏事。

所以这段代码是Pythonic,我怎样才能让它更优雅? (我想这是一个很好的例子,可以帮助我理清python代码的作用,对不起,如果看起来有点开放式结尾)

+0

如果你想understan d更多的实际问题/游戏是显而易见的/progcomp.ucc.asn.au/FrontPage 这些举动是次要的事情,我只是随意地想,我可能想创建一个类型,作为更好的方式来表扬他们。正如我所说的 – 2010-09-16 05:49:32

回答

11

对我来说,代码是“pythonic”的概念真的归结为这样的想法,即一旦你明白了你想要解决什么问题,代码就会自己写出来。在这种情况下,不用担心玩家,游戏,投掷等更深入的抽象,你有以下问题:有一定数量的移动类型,每个移动类型都有一个名称,移动,你需要找到一种方式来定义移动,并找出哪个移动胜过比较。

当我读取你的代码时,我不会立即看到这个问题,我看到很多额外的想法进入代码本身,发现类型表示,做算术技巧,并且通常迫使问题进入代码框架,而不是相反。所以我会建议这样的:


class Move: 
    TYPES = ['rock', 'paper', 'scissors'] 
    BEATS = { 
    'rock': ['scissors'], 
    'paper': ['rock'], 
    'scissors': ['paper'] 
    } 

    def __init__(self, type): 
    if type not in self.TYPES: 
     raise Exception("Invalid move type") 
    self.type = type 

    def __str__(self): 
    return self.type 

    def __cmp__(self, other): 
    if other.type in self.BEATS[self.type]: 
     return 1 
    elif self.type in self.BEATS[other.type]: 
     return -1 
    else: 
     return 0 

而你就完成了。你可以抛出所有其他访问器等,但它实际上只是结冰,核心问题得到解决,代码可读,灵活,易于扩展等。这就是我认为“pythonic”的含义。

+1

如果你真的需要一个整数,使用: def __int __(self): return self.TYPES.index(self.type) – gmarcotte 2010-09-16 06:26:22

2

那么,你只有三种可能的举动,对吧?为什么不把它们表示为字符串?看起来你拥有这些数字的唯一原因是用一些“聪明”的数学来实现比较(即哪个比较失败),但老实说我不认为这是值得的。你真正需要的是确定哪一个是在每一个可能比较赢家功能:

def winner(move0, move1): 
    if move0 == move1: 
     return None 
    elif (move0 == 'rock' and move1 == 'scissors') or \ 
     (...paper vs. rock...) or \ 
     (...scissors vs. paper...): 
     return 0 
    else: 
     return 1 

我只是做了回值None0,并且1作为一个例子,你可以使用任何适合你的情况。

“简单比复杂好,”巨蟒线3 ;-)

+0

。 “我需要匹配一个协议的整数” 世界上还有其他一些东西像我一样投掷动作,而且我会抛出一些动作,而且你知道正在对你做什么的机制就是这个协议。基于这些数字。 蜇伤只是为了方便。 – 2010-09-16 05:40:36

+0

好的,如果它们必须是整数,那么就用整数而不是字符串,即在我的答案中替换''rock'' - >'0'等。它并没有改变任何东西。如果你想打印出一个字符串表示,对于这样简单的事情,我可能只是做一个函数来返回对应给定移动的字符串:'def as_string(move):return {0:“rock”,1:“paper” ,2:“剪刀”} [移动]' – 2010-09-16 06:20:30

0

的禅我不知道游戏是抽象不够好。移动是一个需要两名球员的事件。换句话说,一个动作不是一个球员,而球员不是一个动作。你觉得这个怎么样:

# notice that the element k+1 defeats element k 
THROWS = ['paper', 'scissors', 'rock'] 

class Player(object): 
    def __init__(self, name, throws): 
    # name the player 
    self.name = name 
    # the throws are contained a priori 
    self.throws = throws 

    def throw(self): 
    # a throw uses (and removes) the first element of the throws 
    # list 
    return self.throw_value(self.throws.pop(0)) 

    def throw_value(self, what): 
    if what in [0,1,2]: 
     # if the throw is a legal int, return it 
     return what 
    if what in THROWS: 
     # if the throw is a legal str, return the 
     # corresponding int 
     return THROWS.index(what) 
    # if none of the above, raise error 
    raise ValueError('invalid throw') 

class Game(object): 
    def __init__(self, player_1, player_2): 
    # a game has two players 
    self.player_1 = player_1 
    self.player_2 = player_2 

    def go(self, throws=3): 
    # a "go" of the game throws three times 
    for _ in range(throws): 
     print self.throw() 

    def throw(self): 
    # a throw contains the rules for winning 
    value_1 = self.player_1.throw() 
    value_2 = self.player_2.throw() 

    if value_1 == value_2: 
     return 'draw' 

    if value_1 > value_2: 
     return self.player_1.name 

    return self.player_2.name 

if __name__ == "__main__": 
    juan = Player("Juan", ['rock', 0, 'scissors']) 

    jose = Player("Jose", [1, 'scissors', 2]) 

    game = Game(juan, jose) 

    game.go() 
+0

虽然没有仔细阅读过你的代码,但是,是的,一个动作不是玩家。 游戏正在由另一个应用程序运行, 玩家可以用任何语言编写。 他们通过stdio与数字0,1,2进行交流。 游戏告诉他们,然后他们赢了还是输了。 请参阅:http://progcomp.ucc.asn.au/FrontPage 我只是为我的内部使用移动类来跟踪其他玩家的行为如何 – 2010-09-16 05:44:05

+0

我提出的解决方案也适用于这种情况。无论谁移动播放器,“GAME”的界面都可以工作。 – Escualo 2010-09-16 05:52:48

1
mv = {"Scissor":0, "Rock":1, "Paper":2} 
def winner(m1, m2): 
    result = "Tie" if m1 == m2 else max(m1, m2) if abs(m1 - m2) != (len(mv) - 1) else min(m1, m2) 
    return mv.keys()[mv.values().index(result)] if result in mv.values() else result 

我写这个证明的概念:与5线,几乎没有面向对象就可以实现既定的结果,纸;岩;剪刀。

数字/字符串的字典。如果你通过数字,你的结果将是获胜的字符串的名称。胜利的有效性是顺序的(a < b < c < a),所以你可以简单地进行距离检查以确定是否需要重写序列。我添加了"Tie",因为这是一个明显的例子,但是真正用玩家来构建游戏,并且这种方法都很微不足道。现在,如果你想玩Paper,Rock,Scissors,Lizard,Spock,我们需要重构。

+0

哇。我有点像这样,只要我不必事先知道它做了什么,就不必解释它的作用。 ;) – 2010-09-16 09:52:46

+0

应该有一个 - 最好只有一个 - 明显的方法来做到这一点。 虽然这种方式可能并不明显,除非你是荷兰人。 – Gabriel 2010-09-17 00:28:23

2

下面是一个简短的说明结果的版本。 HTTP:/

def winner(p1, p2): 
    actors = ['Paper', 'Scissors', 'Rock'] 
    verbs = {'RoSc':'breaks', 'ScPa':'cut', 'PaRo':'covers'} 
    p1, p2 = actors.index(p1), actors.index(p2) 
    winner, looser = ((p1, p2), (p2, p1))[(1,0,1)[p1 - p2]] 
    return ' '.join([actors[winner], 
        verbs.get(actors[winner][0:2] + actors[looser][0:2], 
           'ties'), 
        actors[looser]]) 

这种结构的好处,当扩大到摇滚,剪子,布,蜥蜴,斯波克

def winner(p1, p2): 
    actors = ['Paper', 'Scissors', 'Spock', 'Lizard', 'Rock'] 
    verbs = {'RoLi':'crushes', 'RoSc':'breaks', 'LiSp':'poisons', 
      'LiPa':'eats', 'SpSc':'smashes', 'SpRo':'vaporizes', 
      'ScPa':'cut', 'ScLi':'decapitate', 'PaRo':'covers', 
      'PaSp':'disproves'} 
    p1, p2 = actors.index(p1), actors.index(p2) 
    winner, looser = ((p1, p2), (p2, p1))[(1,0,1,0,1)[p1 - p2]] 
    return ' '.join([actors[winner], 
        verbs.get(actors[winner][0:2] + actors[looser][0:2], 
           'ties'), 
        actors[looser]]) 

>>> winner("Rock", "Scissors") 
'Rock breaks Scissors' 
>>> winner("Rock", "Spock") 
'Spock vaporizes Rock' 
>>> winner("Spock", "Paper") 
'Paper disproves Spock' 
>>> winner("Lizard", "Scissors") 
'Scissors decapitate Lizard' 
>>> winner("Paper", "Paper") 
'Paper ties Paper' 
+0

+1 - 通用结果相当不错。尤其是动词。 – Geoff 2013-05-23 21:10:17