14

I heard that you should not use magic methods directly, but I think in some use cases I would have to. Are there any concrete reasons not to do so? (i.e. beyond personal preference)

To be clear, magic methods are also called special or "dunder" (short for double-underline) methods, such as object.__new__() or object.__len__().

7
  • 4
    No you shouldn't. All "magic" methods have an equivalent built-in function or syntax for calling them Commented Apr 5, 2020 at 5:17
  • 4
    You should when you need the specific job they do, but most magic methods are only responsible for part of what you might think their job is. For example, __add__ is not responsible for the entirety of a + operation, __getattribute__ is not the whole attribute access protocol, and not all iterables have an __iter__ method. Commented Apr 5, 2020 at 5:26
  • 3
    One of those relatively rare times when the thing you "heard from one guy" was actually right. Now might be a good time to play the lottery. Commented Apr 5, 2020 at 5:31
  • 1
    The question has been edited to avoid being opinion-based, so please reopen. I also added the official answer (no you should not) to my answer. Commented Aug 9 at 6:22
  • 1
    "I think in some use cases I would have to" what are those cases? Commented Aug 11 at 13:38

4 Answers 4

16

There are benefits to not using magic methods directly:

1- Readability

Using built-in functions like len() is much more readable than its relevant magic/special method __len__(). Imagine a source code full of only magic methods instead of built-in function... thousands of underscores...

2- Comparisons

Comparison operators are more sophisticated than you might think.

class C: def __lt__(self, other): print('__lt__ called') class D: pass c = C() d = D() d > c d.__gt__(c) 

I haven't implemented __gt__ for either of those classes, but in d > c when Python sees that class D doesn't have __gt__, it checks to see if class C implements __lt__. It does, so we get '__lt__ called' in output which isn't the case with d.__gt__(c).

3- Extra checks

class C: def __len__(self): return 'boo' obj = C() print(obj.__len__()) # -> boo print(len(obj)) # TypeError! 

or:

class C: def __str__(self): return 10 obj = C() print(obj.__str__()) # -> 10 print(str(obj)) # TypeError! 

As you see, when Python calls magic methods implicitly, it does some extra checks as well.

4- Speed

This is the least important, but using let's say len() on built-in data types such as str gives a little bit of speedup as compared to __len__():

from timeit import timeit string = 'abcdefghijklmn' n = 10_000_000 print(timeit("len(string)", globals=globals(), number=n)) print(timeit("string.__len__()", globals=globals(), number=n)) 

output (rounded to 2 places):

0.38 0.90 

It's because of the lookup process (__len__ in the namespace), If you create a bound method before timing, it's gonna be faster.

bound_method = string.__len__ print(timeit("bound_method()", globals=globals(), number=n)) 
0.57 
Sign up to request clarification or add additional context in comments.

4 Comments

"My god, it's full of underscores." - Stanley Kubrick or something
Strange, the bound method version is still slower. Maybe len does some sort of shortcutting to the C version of __len__, I'm not sure.
@wjandrea Hi, I most likely measured that before posting back then. So may I ask which version of Python are you using rn ? Things may have changed since then. The bounded method is faster in version 3.8-3.9-3.10, but starting from 3.11 onwards, I can see that the len() is the winner. Thanks to them we no longer need these tricks anymore :)
Tested using 3.12
2

I'm not a senior developer, but my experience says that you shouldn't call magic methods directly.

Magic methods should be used to override a behavior on your object. For example, if you want to define how does your object is built, you override __init__. Afterwards when you want to initialize it, you use MyNewObject() instead of MyNewObject.__init__().

For me, I tend to appreciate the answer given by Alex Martelli here:

When you see a call to the len built-in, you're sure that, if the program continues after that rather than raising an exception, the call has returned an integer, non-negative, and less than 2**31 -- when you see a call to xxx.__len__(), you have no certainty (except that the code's author is either unfamiliar with Python or up to no good;-).

If you want to know more about Python's magic methods, I strongly recommend taking a look on this documentation made by Rafe Kettler: https://rszalski.github.io/magicmethods/

Comments

1

I think in some use cases I would have to use magic methods directly

Nope, there's always a function you can use in the builtins or standard library, e.g.

The exception is when you're writing your own class that implements magic methods. For example, if you're writing your own __init__, then inside it, you may need to call super().__init__() directly.

Regarding your answer

You wrote:

I used them as first-class functions like xlen = x.__mod__

Writing a new function works, as you did, but for better introspection, try partial:

from operator import mod from functools import partial x = 16 xlen = partial(mod, x) 

Usage:

>>> xlen(5) 1 >>> xlen(4) 0 

Introspection:

>>> xlen functools.partial(<built-in function mod>, 16) >>> xlen.func <built-in function mod> >>> xlen.args (16,) 

The main question

Should I use python magic methods directly?

No.

See the language reference on Reserved classes of identifiers:

Any use of __*__ names, in any context, that does not follow explicitly documented use, is subject to breakage without warning.

What's the explicitly documented use of magic methods? See Special method names:

A class can implement certain operations that are invoked by special syntax by defining methods with special names.

(added bold)

  • Note: Some magic methods have helper functions, like len() for __len__, but are also accessed through other syntax, e.g. if x will attempt to call x.__len__ if x has no __bool__.

Comments

0

No you shouldn't.

It's ok to be used in quick-and-dirty code like solutions on HackerRank, but not in production code. When I asked this question, I used them as first-class functions like xlen = x.__mod__ which was more convenient than def xlen(y): return x % y. It's ok to use these kind of snippets in simple programs, but not in any other case.

1 Comment

You can, but it's more Pythonic to use built-ins like len() instead of calling __len__() directly.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.