What makes dunder methods special is their integration with Python's built-in syntax. In the previous lessons, you've learned, for example, about __str__() that gets called when you pass an object to Python's built-in print() function.
In this lesson, you'll learn how you can implement __add__() so that you can add two instances of your Ingredient() class together using the familiar + operator.
Addition
Adding two instances of an object together with the + operator seems like an intuitive way to approach addition as well as string concatenation. That's why Python's built-in data types int and str both have an implementation of their __add__() method that does what you'd expect it to do:
print(2 + 2) # OUTPUT: 4
print("hello" + "world") # OUTPUT: helloworld
As you can see, you're using the same operator (+) in both cases, even though you're working with different data types. Python knows what to do with each of them because of how their __add__() methods are implemented.
Implement Custom Add
You can implement __add__() as well for your custom functions, which gives them access to use the intuitive + operator to work with two instances. Of course, you'll need to decide what it means for your object to add two instances together.
For example, you could decide that adding two Ingredient() objects would mash together their names and create a single new Ingredient() item from the two inputs:
class Ingredient:
"""Models a food item used as an ingredient."""
def __init__(self, name, amount):
self.name = name
self.amount = amount
# -- snip --
def __add__(self, other):
"""Combines two ingredients."""
new_name = self.name + other.name
return Ingredient(name=new_name, amount=1)
In this implementation of __add__(), you've taken two Ingredient() objects as your input, one is the object you'll call the method on, and the other one will be passed as the second argument.
Then you're creating a new_name by concatenating the .name attributes of the two ingredients self and other.
Info: Just like self is only a default name, so is other. You could name it anything else, but it's a good standard to stick with.
Finally, you're instantiating a brand new Ingredient() object that takes the new_name as its name argument and defaults to 1 as its amount. This is also what the method returns.
With this implementation, you're able to add two Ingredient() objects together:
c = Ingredient("carrot", 5)
p = Ingredient("pea", 4)
s = c.__add__(p)
print(s) # OUTPUT: carrotpea (1)
Great! But you didn't end up using the + operator at all. The power of some of the dunder methods is, however, that you don't need to call them explicitly, as you did in the example above. In fact, you shouldn't call them explicitly. That's why they are dunder methods in the first place.
Info: The double-underscore syntax for methods in Python suggests that they are private methods that shouldn't be called directly. This is, however, not enforced and only a standard to work by.
Because Python's + operator is just a shortcut syntax for __add__(), as well as its preferred way of calling this function, you could use the + operator instead for the equivalent effect, but a much more user-friendly experience:
s = c + p
print(s) # OUTPUT: carrotpea (1)
As you might guess correctly, all Python operators are shortcuts to dunder methods that are implemented on the built-in data types in Python. You can implement the equivalent dunder methods on your own custom classes and work with them as you would with built-in types.
That's the power that dunder methods provide you with.
In the next lesson, you'll get to implement what you've learned about classes in Python to your game projects.
Summary: Implementing Operators
- Everything in Python is an object
- All Python operators are implementations of dunder methods
- You can implement/override any dunder method for your custom class