Special Methods in Python
The Python data model is the foundation of how data is represented in Python. It’s the API that allows our objects to play well with the "under the hood" of Python programming. In this article, we’ll be diving into the special methods that can make our classes more Pythonic.
What are Special Methods?
Special methods are class functions with special names that are invoked by special syntax. Defining these special methods in our class definitions can give our class instances some really cool Python powers like iteration, operator overloading, working well with context managers (the ‘with’ keyword), proper string representation and formatting, and many more.
A Simple Game of Rock, Paper, Scissors
Let’s consider a simple game of Rock, Paper, Scissors to demonstrate how we can use special methods to make our code more Pythonic. In this game, we’ll be using the random module to enable the computer to select a random option of either rock, paper, or scissors. We’ll also be using a class approach, where our rock, paper, and scissors will be treated as objects, not string variables.
Class Definition
Our class, RPS, will have two attributes: pick and name. The pick attribute will be used to determine the user’s choice, and the name attribute will be the actual name of the choice.
Defining Special Methods
Let’s add a special method to our class definition. We’ll define the __repr__ method, which will allow us to create a better-looking string representation of our class instance.
Printing the Class Instance
Now, let’s create an instance of our class and test it:
p = RPS('P', 'Paper')
print(p)
This will output: RPS(P, Paper)
Defining the __str__ Method
We can also define the __str__ method to create a more user-friendly string representation of our class instance:
print(p)
This will output: Paper
Defining the __gt__ Method
We can also define the __gt__ method to make our class instances more comparable:
p1 = RPS("P", "Paper")
p2 = RPS("P", "Paper")
p3 = p1
print(p1 > p2) # False
print(p1 > p3) # True
Putting It All Together
Here’s the full implementation of our Python script:
import random
class RPS:
def __init__(self, pick, name):
self.pick = pick
self.name = name
def __repr__(self):
return f"RPS({self.pick}, {self.name})"
def __str__(self):
return self.name
def __gt__(self, other):
return other.pick in {"R": ["S"], "P": ["R"], "S": ["P"]}.get(self.pick, [])
def main():
option_list = ["R", "P", "S"]
user_input = input("Enter your choice (R, P, S): ")
user_input = user_input.upper()
if user_input not in option_list:
print("Invalid choice, try again!")
return
computer_choice = random.choice(option_list)
print(f"You chose {user_input}, computer chose {computer_choice}")
winner = evaluate_winner(user_input, computer_choice)
while not winner:
user_input = input("Enter your choice (R, P, S): ")
user_input = user_input.upper()
if user_input not in option_list:
print("Invalid choice, try again!")
continue
computer_choice = random.choice(option_list)
print(f"You chose {user_input}, computer chose {computer_choice}")
winner = evaluate_winner(user_input, computer_choice)
print(f"Congratulations, you won! {winner}")
def evaluate_winner(user_choice, comp_choice):
if user_choice == comp_choice:
return False
if user_choice in {"R", "S"} and comp_choice == "P":
return True
if user_choice in {"P", "R"} and comp_choice == "S":
return True
return False
if __name__ == "__main__":
main()
Conclusion
In this article, we’ve seen how special methods can make our classes more Pythonic. We’ve defined special methods like __repr__, __str__, and __gt__ to create a better-looking string representation of our class instance, to compare our class instances, and to define how they behave with the greater-than operator. We’ve also seen how we can use special methods to make our code more concise and readable. In the next part of this series, we’ll be exploring operator overloading and making iterable objects.

