代码之家  ›  专栏  ›  技术社区  ›  Frames Catherine White

我怎么能让这个密码蟒蛇

  •  6
  • Frames Catherine White  · 技术社区  · 14 年前

    所以我有一个对象的代码。那个物体是一个你可以在一个石头剪刀的游戏中做出的动作。 现在,为了方便编写和查看,对象需要同时是一个整数(用于匹配协议)和一个字符串。

    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种类型中的一种(或更多,但我不确定它会做什么) 也许我应该使用不同类的对象?把它们做成单件? 另外,我的对象在创建后当前是可以修改的,这在我的头脑中是一件坏事。

    那么这是密码蟒蛇,我怎么才能让它更优雅呢? (我认为这是一个很好的例子,可以帮助我找出什么才是好的Python代码。对不起,如果有点开放的话)

    5 回复  |  直到 14 年前
        1
  •  11
  •   gmarcotte    14 年前

    对我来说,代码是“蟒蛇式”的概念实际上归结为这样一种观点:一旦你理解了你要解决的问题,代码就几乎可以自己写了。在这种情况下,不必担心对玩家、游戏、投掷等的更深层的抽象,你会遇到以下问题:有一定数量的移动类型,每种类型都有一个名字,并为哪种移动设置规则,哪种移动击败哪种其他移动,你需要找到定义移动和数字的方法相比之下,哪一步胜出。

    当我阅读您的代码时,我并没有立即看到这个问题,我看到代码本身有很多额外的想法,找到类型表示,做算术技巧,通常将问题强制到代码框架中,而不是反过来。所以我建议如下:

    
    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
    

    你完成了。你可以放进所有其他的存取器,等等,但它实际上只是一个糖霜,核心问题解决了,代码可读、灵活、易于扩展等等。这就是我认为的“蟒蛇”的真正含义。

        2
  •  2
  •   David Z    14 年前

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

    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
    

    我刚刚计算了回报值 None , 0 1 作为一个例子,您可以使用适合您情况的任何工具。

    “简单胜于复杂,”python行3的禅;-)

        3
  •  2
  •   dansalmo    11 年前

    下面是描述结果的简短版本。

    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'
    
        4
  •  1
  •   Gabriel    14 年前
    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" 因为这是一个明显的例子,但是真正用玩家来构建游戏,用这种方法所有的东西都是微不足道的。现在,如果你想玩纸、石头、剪刀、蜥蜴、斯波克,我们需要重构。

        5
  •  0
  •   Escualo    14 年前

    我不确定这游戏的抽象性是否足够好。一个动作需要两个玩家。换句话说,一个动作不是一个玩家,玩家也不是一个动作。你对此有何看法:

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