In the class tutorial we learned to define a class like this:
classWebsite: def__init__(self, url, founding_year, free_to_use): self.url=urlself.founding_year=founding_yearself.free_to_use=free_to_usedefinfo(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:
>>>classThing: ... def__len__(self): ... return5 ... >>>t=Thing() >>>t<__main__.Thingobjectat0x7f05e4597198>>>>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:
>>>classEmptyThing: ... pass ... >>>deflength(): ... return5 ... >>>e=EmptyThing() >>>e.__len__=length>>>e.__len__() 5>>>len(e) Traceback (mostrecentcalllast): File"<stdin>", line1, in<module>TypeError: objectoftype'EmptyThing'hasnolen() >>>
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)) themessageis'hello'>>>
Combining repr()
with string formatting is also easy.
>>>print(f"the message is {repr(message)}") themessageis'hello'>>>
The __repr__
magic method can be used to customize this. For example, we can do this:
>>>classWebsite: ... def__repr__(self): ... return'<a Website object>' ... >>>w=Website() >>>w.__repr__() '<a Website object>'>>>str(w) '<a Website object>'>>>print(w) <aWebsiteobject>>>>w<aWebsiteobject>>>>
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.
>>>classWebsite: ... def__init__(self, name, founding_year): ... self.name=name ... self.founding_year=founding_year ... def__repr__(self): ... returnf'Website(name={repr(self.name)}, founding_year={repr(self.founding_year)})' ... >>>github=Website('GitHub', 2008) >>>githubWebsite(name='GitHub', founding_year=2008) >>>
This is useful for simple data containers like this Website class.
A description of the object wrapped between
<
and>
.>>>classWebsite: ... def__init__(self, name, founding_year): ... self.name=name ... self.founding_year=founding_year ... def__repr__(self): ... returnf'<Website {repr(self.name)}, founded in {repr(self.founding_year)}>' ... >>>github=Website('GitHub', 2008) >>>github<Website'GitHub', foundedin2008>>>>
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:>>>importrandom>>>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.