A Guide to Python's Magic Methods

Rafe kettler.

Copyright © 2012 Rafe Kettler

Version 1.17

A PDF version of this guide can be obtained from my site or Github . The magic methods guide has a git repository at http://www.github.com/RafeKettler/magicmethods . Any issues can be reported there, along with comments, (or even contributions!).

Table of Contents

Introduction

Construction and initialization, comparison magic methods, numeric magic methods, representing your classes, controlling attribute access, making custom sequences, abstract base classes, callable objects, context managers, building descriptor objects.

  • Pickling your Objects

Appendix 1: How to Call Magic Methods

Appendix 2: changes in python 3.

This guide is the culmination of a few months' worth of blog posts. The subject is magic methods .

What are magic methods? They're everything in object-oriented Python. They're special methods that you can define to add "magic" to your classes. They're always surrounded by double underscores (e.g. __init__ or __lt__ ). They're also not as well documented as they need to be. All of the magic methods for Python appear in the same section in the Python docs, but they're scattered about and only loosely organized. There's hardly an example to be found in that section (and that may very well be by design, since they're all detailed in the language reference , along with boring syntax descriptions, etc.).

So, to fix what I perceived as a flaw in Python's documentation, I set out to provide some more plain-English, example-driven documentation for Python's magic methods. I started out with weekly blog posts, and now that I've finished with those, I've put together this guide.

I hope you enjoy it. Use it as a tutorial, a refresher, or a reference; it's just intended to be a user-friendly guide to Python's magic methods.

Everyone knows the most basic magic method, __init__ . It's the way that we can define the initialization behavior of an object. However, when I call x = SomeClass() , __init__ is not the first thing to get called. Actually, it's a method called __new__ , which actually creates the instance, then passes any arguments at creation on to the initializer. At the other end of the object's lifespan, there's __del__ . Let's take a closer look at these 3 magic methods:

Putting it all together, here's an example of __init__ and __del__ in action:

Making Operators Work on Custom Classes

One of the biggest advantages of using Python's magic methods is that they provide a simple way to make objects behave like built-in types. That means you can avoid ugly, counter-intuitive, and nonstandard ways of performing basic operators. In some languages, it's common to do something like this:

You could certainly do this in Python, too, but this adds confusion and is unnecessarily verbose. Different libraries might use different names for the same operations, making the client do way more work than necessary. With the power of magic methods, however, we can define one method ( __eq__ , in this case), and say what we mean instead:

That's part of the power of magic methods. The vast majority of them allow us to define meaning for operators so that we can use them on our own classes just like they were built in types.

Python has a whole slew of magic methods designed to implement intuitive comparisons between objects using operators, not awkward method calls. They also provide a way to override the default Python behavior for comparisons of objects (by reference). Here's the list of those methods and what they do:

For an example, consider a class to model a word. We might want to compare words lexicographically (by the alphabet), which is the default comparison behavior for strings, but we also might want to do it based on some other criterion, like length or number of syllables. In this example, we'll compare by length. Here's an implementation:

Now, we can create two Word s (by using Word('foo') and Word('bar') ) and compare them based on length. Note, however, that we didn't define __eq__ and __ne__ . This is because this would lead to some weird behavior (notably that Word('foo') == Word('bar') would evaluate to true). It wouldn't make sense to test for equality based on length, so we fall back on str 's implementation of equality.

Now would be a good time to note that you don't have to define every comparison magic method to get rich comparisons. The standard library has kindly provided us with a class decorator in the module functools that will define all rich comparison methods if you only define __eq__ and one other (e.g. __gt__ , __lt__ , etc.) This feature is only available in Python 2.7, but when you get a chance it saves a great deal of time and effort. You can use it by placing @total_ordering above your class definition.

Just like you can create ways for instances of your class to be compared with comparison operators, you can define behavior for numeric operators. Buckle your seat belts, folks...there's a lot of these. For organization's sake, I've split the numeric magic methods into 5 categories: unary operators, normal arithmetic operators, reflected arithmetic operators (more on this later), augmented assignment, and type conversions.

Unary operators and functions

Unary operators and functions only have one operand, e.g. negation, absolute value, etc.

Normal arithmetic operators

Now, we cover the typical binary operators (and a function or two): +, -, * and the like. These are, for the most part, pretty self-explanatory.

Reflected arithmetic operators

You know how I said I would get to reflected arithmetic in a bit? Some of you might think it's some big, scary, foreign concept. It's actually quite simple. Here's an example:

That was "normal" addition. The reflected equivalent is the same thing, except with the operands switched around:

So, all of these magic methods do the same thing as their normal equivalents, except the perform the operation with other as the first operand and self as the second, rather than the other way around. In most cases, the result of a reflected operation is the same as its normal equivalent, so you may just end up defining __radd__ as calling __add__ and so on. Note that the object on the left hand side of the operator ( other in the example) must not define (or return NotImplemented ) for its definition of the non-reflected version of an operation. For instance, in the example, some_object.__radd__ will only be called if other does not define __add__ .

Augmented assignment

Python also has a wide variety of magic methods to allow custom behavior to be defined for augmented assignment. You're probably already familiar with augmented assignment, it combines "normal" operators with assignment. If you still don't know what I'm talking about, here's an example:

Each of these methods should return the value that the variable on the left hand side should be assigned to (for instance, for a += b , __iadd__ might return a + b , which would be assigned to a ). Here's the list:

Type conversion magic methods

Python also has an array of magic methods designed to implement behavior for built in type conversion functions like float() . Here they are:

It's often useful to have a string representation of a class. In Python, there are a few methods that you can implement in your class definition to customize how built in functions that return representations of your class behave.

We're pretty much done with the boring (and example-free) part of the magic methods guide. Now that we've covered some of the more basic magic methods, it's time to move to more advanced material.

Many people coming to Python from other languages complain that it lacks true encapsulation for classes; that is, there's no way to define private attributes with public getter and setters. This couldn't be farther than the truth: it just happens that Python accomplishes a great deal of encapsulation through "magic", instead of explicit modifiers for methods or fields. Take a look:

You can easily cause a problem in your definitions of any of the methods controlling attribute access. Consider this example:

Again, Python's magic methods are incredibly powerful, and with great power comes great responsibility. It's important to know the proper way to use magic methods so you don't break any code.

So, what have we learned about custom attribute access in Python? It's not to be used lightly. In fact, it tends to be excessively powerful and counter-intuitive. But the reason why it exists is to scratch a certain itch: Python doesn't seek to make bad things impossible, but just to make them difficult. Freedom is paramount, so you can really do whatever you want. Here's an example of some of the special attribute access methods in action (note that we use super because not all classes have an attribute __dict__ ):

There's a number of ways to get your Python classes to act like built in sequences ( dict , tuple , list , str , etc.). These are by far my favorite magic methods in Python because of the absurd degree of control they give you and the way that they magically make a whole array of global functions work beautifully on instances of your class. But before we get down to the good stuff, a quick word on requirements.

Requirements

Now that we're talking about creating your own sequences in Python, it's time to talk about protocols . Protocols are somewhat similar to interfaces in other languages in that they give you a set of methods you must define. However, in Python protocols are totally informal and require no explicit declarations to implement. Rather, they're more like guidelines.

Why are we talking about protocols now? Because implementing custom container types in Python involves using some of these protocols. First, there's the protocol for defining immutable containers: to make an immutable container, you need only define __len__ and __getitem__ (more on these later). The mutable container protocol requires everything that immutable containers require plus __setitem__ and __delitem__ . Lastly, if you want your object to be iterable, you'll have to define __iter__ , which returns an iterator. That iterator must conform to an iterator protocol, which requires iterators to have methods called __iter__ (returning itself) and next .

The magic behind containers

Without any more wait, here are the magic methods that containers use:

For our example, let's look at a list that implements some functional constructs that you might be used to from other languages (Haskell, for example).

There you have it, a (marginally) useful example of how to implement your own sequence. Of course, there are more useful applications of custom sequences, but quite a few of them are already implemented in the standard library (batteries included, right?), like Counter , OrderedDict , and NamedTuple .

You can also control how reflection using the built in functions isinstance() and issubclass() behaves by defining magic methods. The magic methods are:

The use case for these magic methods might seem small, and that may very well be true. I won't spend too much more time on reflection magic methods because they aren't very important, but they reflect something important about object-oriented programming in Python and Python in general: there is almost always an easy way to do something, even if it's rarely necessary. These magic methods might not seem useful, but if you ever need them you'll be glad that they're there (and that you read this guide!).

As you may already know, in Python, functions are first-class objects. This means that they can be passed to functions and methods just as if they were objects of any other kind. This is an incredibly powerful feature.

A special magic method in Python allows instances of your classes to behave as if they were functions, so that you can "call" them, pass them to functions that take functions as arguments, and so on. This is another powerful convenience feature that makes programming in Python that much sweeter.

__call__ can be particularly useful in classes with instances that need to often change state. "Calling" the instance can be an intuitive and elegant way to change the object's state. An example might be a class representing an entity's position on a plane:

In Python 2.5, a new keyword was introduced in Python along with a new method for code reuse: the with statement. The concept of context managers was hardly new in Python (it was implemented before as a part of the library), but not until PEP 343 was accepted did it achieve status as a first-class language construct. You may have seen with statements before:

Context managers allow setup and cleanup actions to be taken for objects when their creation is wrapped with a with statement. The behavior of the context manager is determined by two magic methods:

__enter__ and __exit__ can be useful for specific classes that have well-defined and common behavior for setup and cleanup. You can also use these methods to create generic context managers that wrap other objects. Here's an example:

Here's an example of Closer in action, using an FTP connection to demonstrate it (a closable socket):

See how our wrapper gracefully handled both proper and improper uses? That's the power of context managers and magic methods. Note that the Python standard library includes a module contextlib that contains a context manager, contextlib.closing() , that does approximately the same thing (without any handling of the case where an object does not have a close() method).

See http://docs.python.org/2/library/abc.html.

Descriptors are classes which, when accessed through either getting, setting, or deleting, can also alter other objects. Descriptors aren't meant to stand alone; rather, they're meant to be held by an owner class. Descriptors can be useful when building object-oriented databases or classes that have attributes whose values are dependent on each other. Descriptors are particularly useful when representing attributes in several different units of measurement or representing computed attributes (like distance from the origin in a class to represent a point on a grid).

To be a descriptor, a class must have at least one of __get__ , __set__ , and __delete__ implemented. Let's take a look at those magic methods:

Now, an example of a useful application of descriptors: unit conversions.

Sometimes, particularly when dealing with mutable objects, you want to be able to copy an object and make changes without affecting what you copied from. This is where Python's copy comes into play. However (fortunately), Python modules are not sentient, so we don't have to worry about a Linux-based robot uprising, but we do have to tell Python how to efficiently copy things.

What are some use cases for these magic methods? As always, in any case where you need more fine-grained control than what the default behavior gives you. For instance, if you are attempting to copy an object that stores a cache as a dictionary (which might be large), it might not make sense to copy the cache as well -- if the cache can be shared in memory between instances, then it should be.

Pickling Your Objects

If you spend time with other Pythonistas, chances are you've at least heard of pickling. Pickling is a serialization process for Python data structures, and can be incredibly useful when you need to store an object and retrieve it later (usually for caching). It's also a major source of worries and confusion.

Pickling is so important that it doesn't just have its own module ( pickle ), but its own protocol and the magic methods to go with it. But first, a brief word on how to pickle existing types(feel free to skip it if you already know).

Pickling: A Quick Soak in the Brine

Let's dive into pickling. Say you have a dictionary that you want to store and retrieve later. You couldwrite it's contents to a file, carefully making sure that you write correct syntax, then retrieve it using either exec() or processing the file input. But this is precarious at best: if you store important data in plain text, it could be corrupted or changed in any number of ways to make your program crash or worse run malicious code on your computer. Instead, we're going to pickle it:

Now, a few hours later, we want it back. All we have to do is unpickle it:

What happens? Exactly what you expect. It's just like we had data all along.

Now, for a word of caution: pickling is not perfect. Pickle files are easily corrupted on accident and on purpose. Pickling may be more secure than using flat text files, but it still can be used to run malicious code. It's also incompatible across different versions of Python, so don't expect to distribute pickled objects and expect people to be able to open them. However, it can also be a powerful tool for caching and other common serialization tasks.

Pickling your own Objects

Pickling isn't just for built-in types. It's for any class that follows the pickle protocol. The pickle protocol has four optional methods for Python objects to customize how they act (it's a bit different for C extensions, but that's not in our scope):

Our example is a Slate , which remembers what its values have been and when those values were written to it. However, this particular slate goes blank each time it is pickled: the current value will not be saved.

The goal of this guide is to bring something to anyone that reads it, regardless of their experience with Python or object-oriented programming. If you're just getting started with Python, you've gained valuable knowledge of the basics of writing feature-rich, elegant, and easy-to-use classes. If you're an intermediate Python programmer, you've probably picked up some slick new concepts and strategies and some good ways to reduce the amount of code written by you and clients. If you're an expert Pythonista, you've been refreshed on some of the stuff you might have forgotten about and maybe picked up a few new tricks along the way. Whatever your experience level, I hope that this trip through Python's special methods has been truly magical. (I couldn't resist the final pun!)

Some of the magic methods in Python directly map to built-in functions; in this case, how to invoke them is fairly obvious. However, in other cases, the invocation is far less obvious. This appendix is devoted to exposing non-obvious syntax that leads to magic methods getting called.

Hopefully, this table should have cleared up any questions you might have had about what syntax invokes which magic method.

Here, we document a few major places where Python 3 differs from 2.x in terms of its object model:

  • Since the distinction between string and unicode has been done away with in Python 3, __unicode__ is gone and __bytes__ (which behaves similarly to __str__ and __unicode__ in 2.7) exists for a new built-in for constructing byte arrays.
  • Since division defaults to true division in Python 3, __div__ is gone in Python 3
  • __coerce__ is gone due to redundancy with other magic methods and confusing behavior
  • __cmp__ is gone due to redundancy with other magic methods
  • __nonzero__ has been renamed to __bool__

A Guide to Python’s Secret Superpower: Magic Methods

May 31, 2022 • Development

By Corbin Crutchley

python assignment magic method

Python has a secret superpower with a similarly stupendous name: Magic Methods. These methods can fundamentally change the way you code with Python classes and introduce code that seems ✨ magical ✨ to handle complex logic. They’re more powerful than list comprehensions and more exciting than any new PEP8 linter.

Today, we’ll be talking about a few things:

  • What magic methods are
  • Some simple introductory magic method usage

How to programmatically manage class properties

  • How to overwrite operator symbol functionality

How to make your classes iterable

We also have a cheat sheet for utilizing these magic methods quicker within your projects:

Without further ado, let’s dive in!

What are magic methods?

Magic methods are methods that Python calls on your behalf in specific circumstances. These methods are named in a particular way to quickly distinguish them from other Python methods: they’re preceded and followed by two underscores.

This is why magic methods also called “dunder methods,” which is a shorthand for “Double underscore methods.”

In the above code you can see what I’m talking about: Python calls the __init__ dunder method on your behalf when a new class instance is created.

This barely scratches the surface when it comes to the power that magic methods provide. Let’s dive into their usage.

Simple magic method usage

If you’ve ever created a class, you’re likely familiar with the following method:

__init__(self, …args) – ClassName()

It’s probably the best-known magic method, Python’s init acts as a class constructor. You can use this to pass initial arguments to a Python class.

For example, take the following:

Here, whenever the Speaker class is initialized, it will assign self.message to the passed value. We’re then able to use a custom “sayIt” method that utilizes self.message .

Clean up class instantiation with del

In addition to a class initializer, there’s also a class deletion handler:

__del__(self) – del instance

This method will run any time you call del on a class instance. This is particularly useful whenever you have an I/O operation in the constructor in order to cleanup said I/O operations.

This type is cleanup is integral to ensure your applications are deterministic on each run, which in turn increases general application stability. After all, if you leave remnants of your cache, they’re likely to be picked up by subsequent runs and cause havoc with your application logic.

Stuff like class constructors and cleanup are par for the course when it comes to class management. Ready for the weird stuff? What about declaring attributes that don’t exist? __getattr__ has you covered. __getattr__(self, key) – instance.property (when property doesn’t exist)

Simply check what the lookup key is (in this case with the __name property) and return a value if you want to create a new property programmatically:

There also exists a slightly different getattribute built-in:

__getattribute__(self, key) – instance.property (regardless of if property exists)

Notice how instead of test.number returning the expected 1 value, it returns a None .

This is because while __getattr__ will resolve the existing variables and fallback to the special method when nothing is found, __getattribute__ runs first and doesn’t fall back to existing values in the class instance.

In order to have __getattribute__ to have the same behavior as __getattr__ , we need to explicitly tell Python not to get stuck in the __getattribute__ trap we’ve set up.

To do this, we can call super().__getattribute__ :

Customize class property dictionary lookup

While __getattr__ and __getattribute__ both work wonders for adding in keys programmatically, there’s a problem with that method. When using the dir built-in method , it won’t show the new keys.

Let’s show you what I’m talking about with a code sample. Take the following:

This print statement will output all of these keys:

This list of keys includes other magic methods, which muddies the output a bit for our needs. Let’s filter those out with the following logic:

Now, when we run simpledir(test) , we only see:

But where is our ’string’ field? It doesn’t show up.

This is because while we’ve told Python how to look up the overwritten values, we’ve not told Python which keys we’ve added.

To do this, we can use the __dir__ magic method.

__dir__(self) – dir(instance)

Customizing dir behavior like this will now enable us to treat our dynamic properties as if they existed normally. Now all we’re missing is a way to set values to those properties…

Set programmatically created keys

While we’re now telling Python which keys we’re programmatically creating and how to lookup the value of those keys, we’re not telling Python how to store those values.

Take the following code:

Here, we might expect the print(test.string) to output "Test" as well as "Value", since getattr should be called. But, if we look at the log, we only see the following:

This is because, once we assign test.string , it no longer calls getattr the way we expect it to.

To solve this problem, we need to use the __setattr__ magic method to “listen” for property assignment.

__setattr__(self, key, val) – instance.property = newVal

> Notice our usage of super().__setattr__ . We need to do this similarly to how we utilized the super() method in __getattribute__ , otherwise self.updateCount += 1 would trigger an infinite loop of calls to __setattr__ .

Clean up programmatic property instanciation

Just as we can hook into the setting and getting behavior of an attribute, we can also hook into the del behavior of an attribute using __delattr__ .

For example, what if we wanted to create a class that acted like a dictionary. For each key created in this dictionary we’d want to automatically create a temporary file. Then, on cleanup (using del ), let’s remove that file with os.remove :

__delattr__(self, key) – del instance.property

Remember, if you’re not cleaning up your side effects, it may cause havoc with future usage of your app. This is why it’s so important to add in __delattr__ when relevant.

Convert programatic lookups to index properties

In our most recent FileDictionary example, we created a class called “FileDictionary”, but then accessed the child values with the dot accessor:

However, this dot syntax causes some minor headache: it’s not consistent with how you access properties from a dictionary. The reason we’re not using the standard dictionary syntax is because if you do the following:

We would quickly get an error from Python:

> TypeError: ‘FileDictionary’ object is not subscriptable

To solve this problem, we need to migrate away from __setattr__ , which only supports dot notation, to __setitem__ , which only supports the dictionary-style notation.

__getitem__(self, key) – instance[property]

__setitem__(self, key, val) – instance[property] = newVal

__delitem__(self, key)   – del instance[property]

As a wonderful side effect, you’re now able to add in a file extension to the fileDictionry . This is because bracket notation supports non-ASCII symbols while the dot notation does not.

How to replace operator symbol functionality with custom logic

There’s nothing more Pythonic than the simplicity of using simple mathematical symbols to represent mathematic actions.

After all, what could more clearly represent the sum of two numbers than:

Meanwhile, if we have a wrapper around a number:

It gets a bit harder to read through.

What if we could utilize those symbols to handle this custom class logic for us?

Luckily we can!

For example, here’s how we can make the + symbol run custom logic:

__add__(self, other) – instance + other

There’s also other math symbols you can overwrite:

__sub__(self, other) – instance - other

__mul__(self, other) – instance * other

Manage comparison symbol behavior

Addition, subtraction, and multiplication aren’t the only usages for operator overloading, however. We can also modify the comparison operators in Python to run custom logic.

Let’s say we want to check if two strings match, regardless of casing:

__eq__(self, other) – instance == other

You can also have different logic for == and != using __ne__ .

__ne__(self, other) – instance != other

However, if you don’t provide a __ne__ , but do provide a __eq__ , Python will simply negate the __eq__ logic on your behalf when instance != other is called.

There’s also a slew of magic methods for customizing other comparison operators:

__lt__(self, other) – instance < other

__gt__(self, other) – instance > other

__le__(self, other) – instance <= other

__ge__(self, other) – instance >= other

Overwrite a class’s type casting logic

Python, like any other programming language, has the concept of data types. Similarly, you’re able to convert easily from any of those types to another type using built-in methods of type-casting data.

For example, if you call bool() on a string, it will cast the truthy value to a Boolean.

What if you could customize the behavior of the bool() method? You see where we’re going with this…

__bool__(self) – bool(instance)

There’s also other type casts logic you can customize:

__int__(self) – int(instance)

__str__(self) – str(instance)

Let’s say that we’ve used a custom class to build a replacement for a List:

This appears to work amazingly at first glance, until you try to do the following:

Or any other kind of iteration on the ListLike. You’ll get the following confusingly named error:

This is because Python doesn’t know how to iterate through your class, and therefore attempts to access a property in the class. This is where __iter__ comes into play: It allows you to return an iterable to utilize anytime Python might request iterating through the class, like in a list comprehension .

__iter__(self) – [x for x in instance]

Notice that we’re having to return a real list wrapped in the iter method for the __iter__ return value: This is required by Python.  If you don’t do this, you’ll get the error: iter() returned non-iterator of type 'list'

Check if an item exists using the “in” keyword

The __iter__ magic method isn’t the only way to customize traditionally list-like behavior for a class. You can also use the __contains__ method to add support for simple “is this in the class” checks.

__contains__(self, item) – key in instance

Something to keep in mind is that if __contains__ isn’t defined, Python will use the information provided by __iter__ to check if the key is present. However, __contains__ is a more optimized method, since the default __iter__ checking behavior will iterate through every key until it finds a match.

Python magic method cheat sheet

Python magic methods can level up your application logic by reducing the amount of boilerplate required to do specific actions, but that’s not its only usecase. Othertimes, you might want to use magic methods to provide an API with a nicer development experience for consuming developers.

That said, we know that with so many magic methods it can be difficult to remember them all. This is why we made a cheat sheet that you can download or print out to reference when writing code. 

7. Magic Methods

By Bernd Klein . Last modified: 04 Dec 2023.

On this page ➤

Introduction

The so-called magic methods have nothing to do with wizardry. You have already seen them in the previous chapters of our tutorial. They are special methods with fixed names. They are the methods with this clumsy syntax, i.e. the double underscores at the beginning and the end. They are also hard to talk about. How do you pronounce or say a method name like __init__ ? "Underscore underscore init underscore underscore" sounds horrible and is almost a tongue twister. "Double underscore init double underscore" is a lot better, but the ideal way is "dunder init dunder" That's why magic methods methods are sometimes called dunder methods !

So what's magic about the __init__ method? The answer is, you don't have to invoke it directly. The invocation is realized behind the scenes. When you create an instance x of a class A with the statement "x = A()", Python will do the necessary calls to __new__ and __init__ .

Marvin, the magician

Towards the end of this chapter of our tutorial we will introduce the __call__ method. It is overlooked by many beginners and even advanced programmers of Python. It is a functionality which many programming languages do not have, so programmers generally do not look for it. The __call__ method enables Python programmers to write classes where the instances behave like functions. Both functions and the instances of such classes are called callables.

We have encountered the concept of operator overloading many times in the course of this tutorial. We had used the plus sign to add numerical values, to concatenate strings or to combine lists:

Operator Overloading __add__

It's even possible to overload the "+" operator as well as all the other operators for the purposes of your own class. To do this, you need to understand the underlying mechanism. There is a special (or a "magic") method for every operator sign. The magic method for the "+" sign is the __add__ method. For "-" it is __sub__ and so on. We have a complete listing of all the magic methods a little further down.

The mechanism works like this: If we have an expression "x + y" and x is an instance of class K, then Python will check the class definition of K. If K has a method __add__ it will be called with x.__add__(y) , otherwise we will get an error message:

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Overview of Magic Methods

Binary operators, extended assignments, unary operators, comparison operators, example class: length.

We will demonstrate the Length class and how you can overload the "+" operator for your own class. To do this, we have to overload the __add__ method. Our class contains the __str__ and __repr__ methods as well. The instances of the class Length contain length or distance information. The attributes of an instance are self.value and self.unit.

This class allows us to calculate expressions with mixed units like this one:

The class can be used like this:

The listing of the class:

We use the method __iadd__ to implement the extended assignment:

Now we are capable of writing the following assignments:

We added 1 metre in the example above by writing "x += Length(1))". Most certainly, you will agree with us that it would be more convenient to simply write "x += 1" instead. We also want to treat expressions like "Length(5,"yd") + 4.8" similarly. So, if somebody uses a type int or float, our class takes it automatically for "metre" and converts it into a Length object. It's easy to adapt our __add__ and __iadd__ method for this task. All we have to do is to check the type of the parameter "other":

It's a safe bet that if somebody works with adding integers and floats from the right side for a while, he or she will want to have the same from the left side! SWhat will happen, if we execute the following code line:

x = 5 + Length(3, "yd")

We will get an exception:

AttributeError: 'int' object has no attribute 'Converse2Metres'

Of course, the left side has to be of type "Length", because otherwise Python tries to apply the __add__ method from int, which can't cope with Length objects as second arguments!

Python provides a solution for this problem as well. It's the __radd__ method. It works like this: Python tries to evaluate the expression "5 + Length(3, 'yd')". First it calls int. __add__ (5,Length(3, 'yd')), which will raise an exception. After this it will try to invoke Length. __radd__ (Length(3, "yd"), 5). It's easy to recognize that the implementation of __radd__ is analogue to __add__ :

It's advisable to make use of the __add__ method in the __radd__ method:

The following diagram illustrates the relationship between __add__ and __radd__ :

relationship between __add__ and __radd__

Upcoming online Courses

Standard Classes as Base Classes

It's possible to use standard classes - like int, float, dict or lists - as base classes as well.

We extend the list class by adding a push method:

This means that all the previously introduced binary and extended assignment operators exist in the "reversed" version as well: __radd__ , __rsub__ , __rmul__ etc.

Canadian money

Write a class with the name Ccy, similar to the previously defined Length class.Ccy should contain values in various currencies, e.g. "EUR", "GBP" or "USD". An instance should contain the amount and the currency unit. The class, you are going to design as an exercise, might be best described with the following example session:

Solutions to our Exercises

Solution to exercise 1.

Another interesting aspect of this currency converter class in Python can be shown, if we add multiplication. You will easily understand that it makes no sense to allow expressions like "12.4 € * 3.4 USD" (or in prefix notation: "€ 12.4 $ 3.4"), but it makes perfect sense to evaluate "3 4.54 €". You can find the new currency converter class with the newly added methods for __mul__ , __imul__ and __rmul__ in the following listing:

Assuming that you have saved the class under the name currency_converter, you can use it in the following way in the command shell:

  • as suggested by Mark Jackson

On this page

The Magic Methods in Python

Magic Methods In Python

The magic methods in Python programming language are specifically for Object Oriented Design. Every class that we create has its own magic methods. Python’s standard interpreter assigns these to every class we create inside it. So, in this article, we shall see in detail how to call and use magic methods for a better programming approach. Let the coding fun begin!

Brushing up OOP knowledge

Before we get to the main topic, let us understand and polish the knowledge of OOP concepts. We shall see only the basics. So, Object Oriented Programming is a way of enclosing the data members and member functions into a user-defined entity called a Class .

Class is something that holds particular data items that relate to each other and communicate in a specific way. We access the properties and the member functions using Object . The object is an instance of a class. In any programming language, memory is never allocated when we create a class, but it is actually created when we create its instance I.e object.

The animal is a type of class. In that, we include all the living beings that reside on Earth. So, everyone has their own way of living, food, and shelter. The animal just defines the blueprint of all these. For example, the cat is the object of the Animal class. It has four legs, eats mice, and lives in houses or bushes. In the same way, the tiger has four legs but it kills and eats many animals so we say that the tiger eats meat, it lives in the forest.

Code Example with Python:

Explanation:

  • The animal class contains the number of legs, food, and shelter as properties.
  • When we create an instance and insert the values inside the constructor, the difference in their behaviour is clear.
  • So, objects of the same class can be different according to the behaviour of values.

The magic methods in OOP

So, in the above example, we have a class as Animal. Python has a set of methods namely Dunder methods that are responsible for holding the properties, data members, and member functions of a class.

Definition: When we create an object, the Python interpreter calls special functions in the backend of code execution. They are known as Magic Methods or Dunder Methods.

Why do we say Dunder? Because their names lie between double underscores . They perform some set of calculations that are just like magic while we create an object of a class. So, how do we check what are they and how many of them are there in the standard class? Use the steps below to find them:

  • Create a sample class.
  • Create its object.
  • Use the dir() function and insert the object inside it.
  • This function prints a list of all the Magic Methods along with data members and member functions that are assigned to the class.

The names that you see in double underscores are all magic methods. The rest of the properties are defined by the user. As we can see __init__() which is the constructor of any class in Python, is also a magic method. Let us see their use one by one. To understand their functionality always try to override the functions.

One thing to note is that, for any class the user defines, there is some set of default magic methods with respect to each of them.

Use and implementation of some magic methods

In this section, we shall see the use and implementation and use of some magic methods for writing a better OOP design .

1. __new__():

This method helps the constructor __init__() method to create objects for a class. So, when we create an instance of a class, the Python interpreter first calls the __new__() method and after that__init__() method. They work hand in hand with each other.

  • When a programmer opts to create an object, __new__() gets invoked that accepts the name of the object.
  • Then __init__() is invoked where the parameters including self are inserted into the object which in turn helps us to modify the class properties.
  • First, we create a class as Sample.
  • Then override the __new__() method by creating it. Then, as usual, the self parameter comes, and after that gives a simple parameter.
  • Return a super() function with the __new__() function with the self parameter to get access to the customizations we make to the method.
  • Then, with the same practice call the __init__() function with some parameter.
  • After that create an object of a sample class.
  • Now, when we run the code, the interpreter first calls the __new__(), and then it calls __init__() method.

2. __init__():

Python is an Object Oriented Programming language. So, the class must have a constructor. This requirement is fulfilled using the __init__() method. When we create a class and want to give some initial parameters to it. The initializer method performs this task for us.

  • Create/override the __init__() method. Insert self parameter to signal the interpreter that this is a class method.
  • Insert required parameter.
  • Then print that parameter using the print() function.
  • After that create an object.
  • When we run the code we get the output as “init invoked a”, this states that the interpreter calls init() and prints that parameter.

3. __str__():

This method helps us to display the object according to our requirements. So, let us say that when we create an object and try to print it. The print() function displays the memory location of the object. If we want to modify we can do this. The __str__() function gives a nice representation of the object.

Code (before using __str__()):

Code (after using __str__()):

Cool right! now we can also use similar methods. We can format the object as according to our needs.

4. __repr__():

Similar to the __str__(), we can use __repr__ function for the decoration of objects. The code is similar to __str__() implementation.

5. __sizeof__():

When we create a class the interpreter never assigns memory to it. It assigns memory to the object. If we want to know the memory allocated to that object, then we can call or override the __sizeof__() function and pass our object. This also returns size of a list =, tuple, dictionary object.

6. __add__():

This magic method is specifically similar to its name. It adds two variables . For an integer it returns the sum, for a string, it returns their concatenation result.

7. __reduce__():

This magic method returns a set or a dictionary of all the parameters of a class and their values in key: value format. This can be directly called using the object name with the dot operator. So, when we create a class and instantiate it with some values. The function shall return it with the name of the parameters which were given during the declaration of a class.

Code (after overriding __reduce__()):

When we override and try to return the parameters, we only get their values in a set.

8. __hash__():

The __hash__() function returns a specific hash value of the object stored in the heap memory . We can either override it or call it using the object name. Hashing is very useful to fetch the memory address of any random element in a computer. All programming languages use hash for the sake of simplicity and for memory allocation.

9. __getattribute__(name):

This function returns the value of the attribute of a class if it exists. We need to call the function and pass the attribute which we assigned to the class parameter using the self keyword. Like if we assign the value of salary to self.sal we need to call sal inside the __getattribute__() function.

In this function, the “self.sal” is assigned to the salary parameter of the Employee class. The function returns its value as the attribute that exists inside the class. If it does not exist the function returns an error message.

10. __setattr__(name, value):

As the name suggests, this magic method helps us change the value of an attribute when we define the object. No need to override the __getattribute__() and __setattr__() functions. Just call them using the objects created.

  • name of attribute
  • its new value
  • Then it assigns that particular value to that attribute.
  • After that, to check the value assigned to it call the __getattrbute__() function using the employee object and dot operator. emp.__getattribute(“name”).

Point to note: These two functions replace getter and setter methods for a class in Python.

So, we saw the in-depth implementation of some of the magic methods in Python. I hope this helps and will make programming easier. They prove to be helpful pieces of code for quick implementation and use. Happy python programming 🐍🐍😎.

DEV Community

DEV Community

Christopher Thai

Posted on Jun 1

Enhancing Python Classes with Magic Methods: A Comprehensive Guide

Introduction:.

Magic methods, also known as ‘dunder’ methods, are a unique feature in Python that can add a touch of ‘magic’ to your classes. These methods, surrounded by double underscores, are triggered by the Python interpreter under specific circumstances. Also, they enable classes to integrate seamlessly with fundamental Python operations. They can be used for various tasks, such as converting an object to a string or adding two objects together. For instance, the ‘str’ method can describe how an object should be represented as a string, and the ‘add’ method can define how two objects should be added together.

Python interpreters invoke these methods in specific scenarios. A typical example is the __init__() method, which initializes a new object, __repr__() and __str__() , which are used for representing the object as a string for developers and users, respectively. Through magic methods, Python allows objects to overload standard operations, a concept where an object can redefine how an operator or built-in function works with that object. This capability is a powerful tool that improves the code’s efficiency and functionality, ensuring that objects work with Python built-in functions effectively and intuitively.

This blog will explore how leveraging magic methods can improve the utility of custom classes, making them more versatile and robust than Python’s core types and promoting their deeper integration with the language’s features. This leads to more cleaner and maintainablity code and enables developers to implement advanced object-oriented designs that interact naturally with Python’s own structures.

Understanding Magic Methods:

Magic methods provide a way to define how your objects should interact with various aspects of Python, such as functions, statements, and operators. They are the mechanism behind the sense for many of Python’s built-in functionalities, and by defining these methods in your classes, you can leverage Python’s intuitive and concise styles.

Essential Magic Methods and Their Implementations:

Constructor and initializer: __new__ and __init__.

  • __new__(cls, …): Called to create a new instance of a class. __new__ is rarely used but is essential for immutable or complex instances where you need to control the creation before initialization.
  • __init__(self, …): Used to initialize a new object after it’s been created.

String Representation: __str__ and __repr__

  • __str__(self): Defines the user-friendly string representation of an object, and is used by the str() function and print.
  • __repr__(self): Intended for developers, used for debugging and development, should be as explicit as possible and, if feasible, match the code necessary to recreate the object.

Arithmetic Operations: __add__ , __sub__ , etc.

  • Define behavior for all arithmetic operators (+, , , /) .
  • __add__(self, other): Allows two objects to be added together using + .

Comparison Magic Methods: __eq__ , __lt__ , etc.

  • __eq__(self, other): Defines behavior for the equality operator == .
  • Other comparison methods include __ne__ , __lt__ , __le__ , __gt__ , __ge__ .

Container Methods: __len__ , __getitem__ , __setitem__ , and __delitem__

  • These methods allow your objects to act like containers.
  • __len__(self): Return the length of the container.
  • __getitem__(self, key): Define behavior for accessing an item (container[key]) .
  • __setitem__(self, key, value): Define behavior for setting an item (container[key] = value) .
  • __delitem__(self, key): Define behavior for deleting an item (del container[key]) .

Practical Example: Creating a Complex Number Class with Magic Methods

Let’s bring some of these concepts together by creating a class that represents complex numbers and uses several magic methods to allow mathematical operations and more:

In this example, the ComplexNmber class allows additions and multiplication of complex numbers, integrating seamlessly with Python’s syntax.

Conclusion:

Magic methods are critical to Python programming, serving as a bridge that allows custom objects to emulate the behavior of bulti-in types. This feature enriches the language by offering to improve functionality and effortlessly integrate with Python’s core operations. When developers incorporate these unique methods, characterized by their double underscore prefixes and suffice, into their classes, they naturally empower their code to interact with basic Python operators and functions. This will result in more maintainable and intuitive codes, significantly improving the readability and performance of software applications. So, implementing magic methods can ensure that custom objects adhere to Pythong’s elegant syncs and thrive within its operational paradigm, thus elevating the overall programming experience.

Further Exploration:

The capabilities of magic methods extend far beyond what has been discussed in this blog. They provide a foundational framework that invites further experimentation and exploration. For instance, methods like __enter__ and __exit__ are crucial for context management, facilitating using the “with” statement to manage resources efficiently. Additionally, the __call__ method can make an object callable, just like a function, which opens up creative possibilities for designing flexible and modular code. Exploring these and other magic methods can unlock advanced functionality and enable developers to create more sophisticated and robust systems within the Python environment. Engaging with these more profound aspects of Python’s object models can encourage a better utilization and understanding of the Python language’s extensive features, which can drive innovation and expertise in Python programming.

Top comments (0)

pic

Templates let you quickly answer FAQs or store snippets for re-use.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink .

Hide child comments as well

For further actions, you may consider blocking this person and/or reporting abuse

rcmgleite profile image

Build your own Dynamo-like key/value database - Part 1 - TCP Server

Rafael Camargo Leite - Nov 12

keploy profile image

Understanding the HTTP 500 Internal Server Error: Causes, Solutions, and Prevention

keploy - Nov 12

visdom_04_88f1c6e8a47fe74 profile image

Agentic Mesh: Pioneering the Future of Autonomous Agent Ecosystems

VISDOM 04 - Nov 12

lightningchart profile image

LightningChart Python 1.0

Omar Urbano | LightningChart - Nov 12

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

IMAGES

  1. Python Magic Methods Cheat Sheet

    python assignment magic method

  2. The Magic Methods in Python

    python assignment magic method

  3. Magic Methods in Python

    python assignment magic method

  4. Python Magic Method

    python assignment magic method

  5. Understanding Python: Magic Methods

    python assignment magic method

  6. Magic Methods in Python, by example

    python assignment magic method

VIDEO

  1. Python Experts Love Using THIS Magic Method

  2. Day 43: Understanding Dunder (Magic) Functions in Python OOP

  3. Magic in python String representation #coding #pythonbangla #programming #pythonbanglatutorial

  4. THIS Magic Method Is AWESOME #code #programming #python

  5. Become a Python Expert: Secrets and Guide of Assignment Operator

  6. 05 CYU ASGN Magic 8 Ball

COMMENTS

  1. Is it possible to overload Python assignment?

    I'm not sure if things are different for my version / implementation of python, but for me this works only when trying to access variables form outside of the protected module; i.e. if I protect the module tst and assign Protect() to a variable named var twice within the module tst, no exception is raised.This is in line with the documentation stating that direct assignment utilizes the non ...

  2. A Guide to Python's Magic Methods « rafekettler.com

    Augmented assignment. Python also has a wide variety of magic methods to allow custom behavior to be defined for augmented assignment. You're probably already familiar with augmented assignment, it combines "normal" operators with assignment. ... Again, Python's magic methods are incredibly powerful, and with great power comes great ...

  3. Python's Magic Methods: Leverage Their Power in Your Classes

    Magic methods exist for many purposes. All the available magic methods support built-in features and play specific roles in the language. For example, built-in types such as lists, strings, and dictionaries implement most of their core functionality using magic methods. In your custom classes, you can use magic methods to make callable objects, define how objects are compared, tweak how you ...

  4. Introducing Python Magic Methods

    For example, __setattr__ is an encapsulation solution that enables us to define behavior for assignment to an attribute despite its presence. In other words, rules can be defined for changes in the values of attributes. ... Another category of Python magic methods are the math magic methods (also known as normal arithmetic operators ...

  5. A Guide to Python's Secret Superpower: Magic Methods

    It's probably the best-known magic method, Python's init acts as a class constructor. You can use this to pass initial arguments to a Python class. For example, take the following: ... To solve this problem, we need to use the __setattr__ magic method to "listen" for property assignment.

  6. 7. Magic Methods

    To do this, you need to understand the underlying mechanism. There is a special (or a "magic") method for every operator sign. The magic method for the "+" sign is the __add__ method. For "-" it is __sub__ and so on. We have a complete listing of all the magic methods a little further down.

  7. Demystifying Python's Magic Methods

    Python's magic methods provide a powerful way to customize your objects, allowing them to emulate built-in types, interact with operators, handle context management, and much more. ... But Python has even more magic methods for you to explore, including those for unary operators, compound assignment operators, and others. Each has its unique ...

  8. The Magic Methods in Python

    The magic methods in Python programming language are specifically for Object Oriented Design. Every class that we create has its own magic methods. Python's standard interpreter assigns these to every class we create inside it. So, in this article, we shall see in detail how to call and use magic methods for a better programming approach.

  9. Enhancing Python Classes with Magic Methods: A Comprehensive Guide

    Through magic methods, Python allows objects to overload standard operations, a concept where an object can redefine how an operator or built-in function works with that object. This capability is a powerful tool that improves the code's efficiency and functionality, ensuring that objects work with Python built-in functions effectively and ...

  10. How to Use Python Magic Methods to Enhance Skill

    some commonly used magic methods, along with their use cases 1. Constructor Magic: __new__ and __init__. In Python, the magic methods __new__ and __init__ serve distinct purposes in the object creation and initialization processes: __new__ is a static method that is responsible for creating a new instance of a class.