Skip to content

Commit d5f9798

Browse files
committed
magic methods
1 parent 0a37e10 commit d5f9798

File tree

2 files changed

+260
-1
lines changed

2 files changed

+260
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ techniques just because you know how to use them.** Prefer the simple
6161
techniques from the Basics part instead when possible. Simple is better
6262
than complex.
6363

64-
1. [Iterables and iterators](advanced/iterators.md)
64+
1. [Magic methods](advanced/magicmethods.md)
65+
2. [Iterables and iterators](advanced/iterators.md)
6566

6667
### Other things this tutorial comes with
6768

advanced/magicmethods.md

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
# Magic methods
2+
3+
In [the class tutorial](../basics/classes.md) we learned to define a
4+
class like this:
5+
6+
```py
7+
class Website:
8+
9+
def __init__(self, url, founding_year, free_to_use):
10+
self.url = url
11+
self.founding_year = founding_year
12+
self.free_to_use = free_to_use
13+
14+
def info(self):
15+
print("URL:", self.url)
16+
print("Founding year:", self.founding_year)
17+
print("Free to use:", self.free_to_use)
18+
```
19+
20+
After doing that we can create a new Website object like
21+
`Website('https://github.com/', 2008, True)`. Python first creates the
22+
Website object, and then calls `__init__` with the arguments we passed
23+
to Website to set it up. Methods that have a name `__like_this__` and a
24+
special meaning are called **magic methods** or **special methods**.
25+
26+
Most magic methods define what the object has or what it can do, like
27+
"does it have a length" or "can we for loop over it". There are other
28+
magic methods that do other things also, like `__init__`.
29+
30+
Some magic methods have a default implementation that is used if the
31+
class doesn't define anything else. For example, if we don't define an
32+
`__init__` then our class will take no arguments and it won't have any
33+
attributes by default. We'll learn more about this when we'll talk about
34+
[inheritance](classes2.md).
35+
36+
**TODO:** write a `classes2.md`.
37+
38+
## Custom len()
39+
40+
Let's get started by defining an object that has a length:
41+
42+
```py
43+
>>> class Thing:
44+
... def __len__(self):
45+
... return 5
46+
...
47+
>>> t = Thing()
48+
>>> t
49+
<__main__.Thing object at 0x7f05e4597198>
50+
>>> t.__len__()
51+
5
52+
>>> len(t)
53+
5
54+
>>>
55+
```
56+
57+
This is what most magic methods are like. So far we have learned to use
58+
`len()` with lists, strings and other built-in types, but now we can
59+
call `len()` on our own Thing object. Many things can be fully
60+
customized with magic methods.
61+
62+
Note that magic methods like `__len__` need to be defined in the class,
63+
just attaching an attribute called `__len__` doesn't work:
64+
65+
```py
66+
>>> class EmptyThing:
67+
... pass
68+
...
69+
>>> def length():
70+
... return 5
71+
...
72+
>>> e = EmptyThing()
73+
>>> e.__len__ = length
74+
>>> e.__len__()
75+
5
76+
>>> len(e)
77+
Traceback (most recent call last):
78+
File "<stdin>", line 1, in <module>
79+
TypeError: object of type 'EmptyThing' has no len()
80+
>>>
81+
```
82+
83+
You don't really need to worry about why Python works like this, but
84+
it's explained
85+
[here](https://docs.python.org/3/reference/datamodel.html#special-method-lookup)
86+
if you want to know more about it.
87+
88+
## Custom str() and repr()
89+
90+
You have probably noticed that typing something to the interactive `>>>`
91+
prompt is not quite the same thing as printing it. For example,
92+
strings behave like this:
93+
94+
```py
95+
>>> 'hello'
96+
'hello'
97+
>>> print('hello')
98+
hello
99+
>>>
100+
```
101+
102+
Typing `'hello'` without a print displayed it with quotes, but printing
103+
it displayed it without the quotes. Both of these can be customized with
104+
magic methods. `print('hello')` does the same thing as
105+
`print(str('hello'))`, and typing `'hello'` without a print does roughly
106+
the same thing as `print(repr('hello'))`.
107+
108+
```py
109+
>>> repr('hello') # a string that literally contains 'hello'
110+
"'hello'"
111+
>>> str('hello') # same as just 'hello'
112+
'hello'
113+
>>> print(repr('hello'))
114+
'hello'
115+
>>> print(str('hello'))
116+
hello
117+
>>>
118+
```
119+
120+
As usual, `repr(thing)` does the same thing as `thing.__repr__()` and
121+
`str(thing)` does the same thing as `thing.__str__()`. Usually you don't
122+
need to and you shouldn't define `__str__` yourself because `__str__`
123+
defaults to `__repr__`.
124+
125+
Here's an example that hopefully clarifies things:
126+
127+
```py
128+
>>> class Website:
129+
... def __repr__(self):
130+
... return '<a Website object>'
131+
...
132+
>>> w = Website()
133+
>>> w.__repr__()
134+
'<a Website object>'
135+
>>> repr(w)
136+
'<a Website object>'
137+
>>> w.__str__()
138+
'<a Website object>'
139+
>>> str(w)
140+
'<a Website object>'
141+
>>> w
142+
<a Website object>
143+
>>> print(w)
144+
<a Website object>
145+
>>>
146+
```
147+
148+
The `repr()` values can also be easily used with string formatting.
149+
Instead of this...
150+
151+
```py
152+
>>> message = 'hello'
153+
>>> print("message is " + repr(message))
154+
message is 'hello'
155+
>>>
156+
```
157+
158+
...you can do this:
159+
160+
```py
161+
>>> print("message is %r" % message)
162+
message is 'hello'
163+
>>> print("message is {!r}".format(message))
164+
message is 'hello'
165+
>>>
166+
```
167+
168+
The `__repr__` method can return any string, but usually you should
169+
follow one of these styles:
170+
171+
1. A piece of code that describes how another, similar object can be
172+
created.
173+
174+
```py
175+
>>> class Website:
176+
... def __init__(self, name, founding_year):
177+
... self.name = name
178+
... self.founding_year = founding_year
179+
... def __repr__(self):
180+
... return 'Website(name=%r, founding_year=%r)' % (
181+
... self.name, self.founding_year)
182+
...
183+
>>> github = Website('GitHub', 2008)
184+
>>> github
185+
Website(name='GitHub', founding_year=2008)
186+
>>>
187+
```
188+
189+
This is useful for simple data containers like this Website class.
190+
191+
2. A description of the object wrapped between `<` and `>`.
192+
193+
```py
194+
>>> class Website:
195+
... def __init__(self, name, founding_year):
196+
... self.name = name
197+
... self.founding_year = founding_year
198+
... def __repr__(self):
199+
... return '<Website %r, founded in %r>' % (
200+
... self.name, self.founding_year)
201+
...
202+
>>> github = Website('GitHub', 2008)
203+
>>> github
204+
<Website 'GitHub', founded in 2008>
205+
>>>
206+
```
207+
208+
This style is good when you want to tell more about the object than
209+
you can by showing the `__init__` arguments. Python's built-in
210+
things also use this style more:
211+
212+
```py
213+
>>> import random
214+
>>> random
215+
<module 'random' from '/some/path/random.py'>
216+
>>>
217+
```
218+
219+
## Other magic methods
220+
221+
There are many more magic methods, and I don't see any reason to list
222+
them all here. [The official
223+
documentation](https://docs.python.org/3/reference/datamodel.html) has
224+
more information about magic methods if you need it. You can also just
225+
keep reading this tutorial, and we'll learn more about some of the most
226+
useful and commonly used magic methods.
227+
228+
## When should we use magic methods?
229+
230+
Usually magic methods are overkill. `website.has_user(user)` or
231+
`user in website.userlist` is way better than something weird like
232+
`user @ website`. People expect `website.has_user(user)` to check if
233+
the user has registered on the website, but nobody knows what
234+
`user @ website` does. Explicit is better than implicit, and simple is
235+
better than complex.
236+
237+
On the other hand, adding informative `__repr__` methods can make a
238+
module much nicer to use. I recommend using `__repr__` methods in things
239+
that other people will import and use in their projects, but `__repr__`
240+
methods aren't worth it for simple scripts that aren't meant to be
241+
imported.
242+
243+
## Summary
244+
245+
- Magic methods define what instances of a class can do and how, like
246+
"does it have a length" or "what does it look like when I print it".
247+
- Python uses magic methods to implement many things internally.
248+
- Defining custom `__repr__` methods is often a good idea when making
249+
things that other people will import and use in their own projects.
250+
Other than that, magic methods are usually not worth it.
251+
252+
***
253+
254+
You may use this tutorial freely at your own risk. See
255+
[LICENSE](../LICENSE).
256+
257+
[Previous](../basics/classes.md) | [Next](iterators.md) |
258+
[List of contents](../README.md#advanced)

0 commit comments

Comments
 (0)