Как я могу сделать этот код Pythonic - PullRequest
6 голосов
/ 16 сентября 2010

Итак, у меня есть этот код для объекта.Этот объект является движением, которое вы можете совершить в игре с ножницами из каменной бумаги.Теперь объект должен быть как целым числом (для сопоставления протокола), так и строкой для удобства записи и просмотра.

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 очень важно то, что есть только один правильный способ что-то сделать.Другое дело не беспокоиться о типе.Я довольно явно беспокоюсь о типе здесь.

Так что я не уверен, как правильно обращаться с этими объектами.На данный момент у меня есть объект, который может быть одним из любых трех типов (или более, но я не уверен, что это будет делать). Может быть, вместо этого мне следует использовать объекты разных классов?и сделать их одиночками?Кроме того, мой объект в настоящее время модифицируется после создания, что, на мой взгляд, плохо.

Итак, является ли этот код Pythonic, и как я могу сделать его более элегантным?(Я полагаю, что это хороший пример, чтобы помочь мне понять, что делает хороший код на Python. Извините, если он кажется немного открытым)

Ответы [ 5 ]

11 голосов
/ 16 сентября 2010

Для меня концепция кода как «питонического» действительно сводится к мысли, что, как только вы понимаете, какую проблему вы пытаетесь решить, код практически сам себя пишет. В этом случае, не беспокоясь о более глубоких абстракциях игроков, игр, бросков и т. Д., У вас возникает следующая проблема: существует определенное количество типов ходов, каждый с именем, с установленными правилами, для которых ходы бьют, какие другие ходы, и вам нужно найти способ определить ходы и выяснить, какой ход выигрывает в сравнении.

Когда я читаю ваш код, я не сразу вижу эту проблему, я вижу много дополнительных мыслей, которые были направлены на сам код, поиск представлений типов, выполнение арифметических трюков и, как правило, встраивание проблемы в структуру кода, а не наоборот. Поэтому я бы предложил что-то вроде:


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».

2 голосов
/ 22 мая 2013

Вот короткая версия, которая озвучивает результат.

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'
2 голосов
/ 16 сентября 2010

Ну, у вас есть только три возможных хода, верно?Почему бы просто не представить их как строки?Кажется, единственная причина, по которой у вас есть цифры, состоит в том, чтобы проводить сравнения (то есть, какие из них побеждают) с помощью некоторой «умной» математики, но, честно говоря, я не думаю, что это того стоит.Все, что вам действительно нужно, - это функция, определяющая, кто из них является победителем в каждом возможном сравнении:

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 в качестве примера, вы можете использовать все, что подходит для вашей ситуации.

«Простое лучше, чем сложное», строка 3 Дзэн Python; -)

1 голос
/ 16 сентября 2010
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 "Tie", так как это очевидный случай, но на самом деле построение игры с игроками, и с этим методом все тривиально.Теперь, если вы хотите сыграть в «Бумагу, Камень, Ножницы, Ящерицу, Спока», нам потребуется рефакторинг.

0 голосов
/ 16 сентября 2010

Я не уверен, что игра достаточно хорошо абстрагирована. Ход - это событие, в котором принимают участие два игрока. Другими словами, ход не игрок, и игрок не ход. Что вы думаете об этом:

# 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...