How can I make this pythonic code

So, I have this code for an object. This object is a step you can take in a game with rock paper scissors. Now the object should be both an integer (to comply with the protocol) and a string for ease of recording and viewing.

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; 

Now I am new to python based on the background of C and Java. In python, the big deal is that there is only one right way to do something. Another thing is not to worry about the type. I'm pretty obviously worried about the type here.

So, I'm not sure what the correct way to handle these objects is. At the moment, I have an object that will be one of three types (or more, but I'm not sure what it will do) Maybe I should use objects of different classes instead? and make them single? Also my object is being modified after creation, which is bad in my head.

So this is Pythonic code, and how can I make it more elegant? (I believe this is a good example to use to help me figure out what good Python code does. Sorry if it seems a little open)

+6
python oop coding-style
source share
5 answers

For me, the concept of code, which is "pythonic", really comes down to the idea that as soon as you understand what problem you are trying to solve, the code is almost written down by itself. In this case, without worrying about deeper abstractions of players, games, throws, etc., you have the following problem: there are a certain number of types of moves, each with a name, with established rules for which the moves beat, the moves, and you you need to find a way to determine the moves and find out which movement wins when comparing.

When I read your code, I don’t immediately see this problem, I see a lot of unnecessary thoughts that fell into the code itself, found type representations, did arithmetic tricks and generally made the problem within the code, and not vice versa. So I would suggest something like:

 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 

And you're done. You can use all other accessors, etc., but in fact it’s just icing, the main problem is solved, and the code is read, flexible, easily extensible, etc. This is truly what I consider "pythonic."

+11
source share

Well, you only have three possible steps, right? Why not just represent them as strings? It seems that the only reason you have numbers is to implement comparisons (i.e., which win) with some β€œsmart” math, but to be honest, I don't think it's worth it. All you really need is a function that determines which one is the winner in every possible comparison:

 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 

I just compiled the return values None , 0 and 1 as an example, you can use whatever suits your situation.

"Simple is better than complex," Zen of Python line 3; -)

+2
source share

Here is a short version that verbalizes the result.

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

The advantage of this structure is evident when expanded to cover stones, paper, scissors, lizards, 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' 
+2
source share
 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 

I wrote this to prove the concept: with 5 lines and almost no object orientation, you can achieve the stated result, paper; a rock; scissor-shaped.

Dictionary of numbers / lines. If you pass the numbers, your result will be the name of the winning line. The payoff reality is sequential (a <b <c <a), so you can just do a remote check to determine if you need to redefine the sequence. I added "Tie" as this is an obvious case, but really creating a game with players, and all this is trivial with this method. Now, if you want to play Paper, Rock, Scissors, Lizard, Spock, we will need to reorganize.

+1
source share

I'm not sure that the game is abstracted well enough. Movement is an event in which two players are received. In other words, the movement is not a player, and the player is not moving. What do you think about this:

 # 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
source share

All Articles