In the class tutorial we learned to define a class like this:
class Website:
def __init__(self, url, founding_year, free_to_use):
self.url = url
self.founding_year = founding_year
self.free_to_use = free_to_use
def info(self):
print("URL:", self.url)
print("Founding year:", self.founding_year)
print("Free to use:", self.free_to_use)
After doing that we can create a new Website object like
Website('https://github.com/', 2008, True)
. Python first creates the
Website object, and then calls __init__
with the arguments we passed
to Website to set it up. Methods that have a name __like_this__
and a
special meaning are called magic methods or special methods.
Most magic methods define what the object has or what it can do, like
"does it have a length" or "can we for loop over it". There are other
magic methods that do other things also, like __init__
.
Some magic methods have a default implementation that is used if the
class doesn't define anything else. For example, if we don't define an
__init__
then our class will take no arguments and it won't have any
attributes by default. We'll learn more about this when we'll talk about
inheritance.
TODO: write a classes2.md
.
Let's get started by defining an object that has a length:
>>> class Thing:
... def __len__(self):
... return 5
...
>>> t = Thing()
>>> t
<__main__.Thing object at 0x7f05e4597198>
>>> t.__len__()
5
>>> len(t)
5
>>>
This is what most magic methods are like. So far we have learned to use
len()
with lists, strings and other built-in types, but now we can
call len()
on our own Thing object. Many things can be fully
customized with magic methods.
Note that magic methods like __len__
need to be defined in the class,
just attaching an attribute called __len__
doesn't work:
>>> class EmptyThing:
... pass
...
>>> def length():
... return 5
...
>>> e = EmptyThing()
>>> e.__len__ = length
>>> e.__len__()
5
>>> len(e)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'EmptyThing' has no len()
>>>
You don't really need to worry about why Python works like this, but it's explained here if you want to know more about it.
You have probably noticed that typing something to the interactive >>>
prompt is not quite the same thing as printing it. For example,
strings behave like this:
>>> 'hello'
'hello'
>>> print('hello')
hello
>>>
If you want to print something the way it's displayed on the >>>
prompt you can use the repr()
function. Here "repr" is short for
"representation".
>>> message = 'hello'
>>> print("the message is", repr(message))
the message is 'hello'
>>>
Combining repr()
with string
formatting is also
easy.
>>> print(f"the message is {repr(message)}")
the message is 'hello'
>>>
The __repr__
magic method can be used to customize this. For example,
we can do this:
>>> class Website:
... def __repr__(self):
... return '<a Website object>'
...
>>> w = Website()
>>> w.__repr__()
'<a Website object>'
>>> str(w)
'<a Website object>'
>>> print(w)
<a Website object>
>>> w
<a Website object>
>>>
The __repr__
method can return any string, but usually you should
follow one of these styles:
-
A piece of code that describes how another, similar object can be created.
>>> class Website: ... def __init__(self, name, founding_year): ... self.name = name ... self.founding_year = founding_year ... def __repr__(self): ... return f'Website(name={repr(self.name)}, founding_year={repr(self.founding_year)})' ... >>> github = Website('GitHub', 2008) >>> github Website(name='GitHub', founding_year=2008) >>>
This is useful for simple data containers like this Website class.
-
A description of the object wrapped between
<
and>
.>>> class Website: ... def __init__(self, name, founding_year): ... self.name = name ... self.founding_year = founding_year ... def __repr__(self): ... return f'<Website {repr(self.name)}, founded in {repr(self.founding_year)}>' ... >>> github = Website('GitHub', 2008) >>> github <Website 'GitHub', founded in 2008> >>>
This style is good when you want to tell more about the object than you can by showing the
__init__
arguments. Python's built-in things also use this style more:>>> import random >>> random <module 'random' from '/some/path/random.py'> >>>
There are many more magic methods, and I don't see any reason to list them all here. The official documentation has more information about magic methods if you need it. We'll go through using the most important magic methods in the rest of this tutorial, so if you just keep reading you'll learn more about them.
There's nothing wrong with using __init__
everywhere, but other than
that, magic methods are usually not needed. website.has_user(user)
and
user in website.userlist
are way better than something weird that we
could do with magic methods like user @ website
. People expect
website.has_user(user)
check if a user has registered on the website,
but nobody can guess what user @ website
does. Explicit is better than
implicit, and simple is better than complex.
On the other hand, using magic methods when needed can turn something
good into something great. Especially the __repr__
method is useful
because people can get a good idea of what an object is by just looking
at it on the >>>
prompt or printing it. I recommend using __repr__
methods in things that other people will import and use in their
projects, but __repr__
methods aren't worth it for simple scripts that
are not meant to be imported.
- Magic methods define what instances of a class can do and how, like "does it have a length" or "what does it look like when I print it".
- Python uses magic methods to implement many things internally, and we can customize everything by implementing the magic methods ourselves.
- Defining custom
__repr__
methods is often a good idea when making things that other people will import and use in their own projects, and the__init__
method is very useful for many things. Other than that, magic methods are usually not worth it.
If you have trouble with this tutorial, please tell me about it and I'll make this tutorial better, or ask for help online. If you like this tutorial, please give it a star.
You may use this tutorial freely at your own risk. See LICENSE.