簡體   English   中英

我如何使這段代碼為Pythonic

[英]How can I make this code 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代碼。很抱歉,如果它看起來有點開放)

對我來說,代碼是“ 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”的意思。

這是說明結果的簡短版本。

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]])

當擴展到涵蓋岩石,紙張,剪刀,蜥蜴,Spock時,這種結構的好處顯而易見。

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'

好吧,您只有三個可能的舉動,對吧? 為什么不僅僅將它們表示為字符串? 似乎您唯一擁有數字的唯一原因是用一些“聰明”的數學方法來進行比較(即哪個比較失敗),但是老實說我不認為這是值得的。 您真正需要的只是一個函數,它確定每次比較中的贏家:

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

我僅以返回值None01為例,您可以使用適合您情況的任何值。

“簡單總比復雜要好,” Python Zen 3行;-)

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,剪刀,Lizard,Spock,我們將需要重構。

我不確定游戲是否足夠抽象。 動作是需要兩名玩家參加的賽事。 換句話說,一個舉動不是玩家,而玩家也不是舉動。 你怎么看待這件事:

# 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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM