所以我有一个对象的代码。那个对象是你可以在摇滚纸剪刀游戏中做出的举动。 现在,对象需要是一个整数(用于匹配协议)和一个字符串,以方便编写和查看。
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 :(得分: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 :(得分: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
我只是将返回值None
,0
和1
作为示例,您可以使用适合您情况的任何内容。
“简单比复杂更好”,Python第3行的Zen; - )
答案 2 :(得分:2)
这是一个用语言表达结果的简短版本。
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]])
当扩展到Rock,Paper,Scissors,Lizard,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'
答案 3 :(得分: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 "Tie",因为这是一个明显的案例,但真正构建了与玩家的游戏,这一切都是微不足道的。现在,如果你想玩Paper,Rock,Scissors,Lizard,Spock,我们需要重构。
答案 4 :(得分: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()