2010-09-16 57 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