Difference between len() and .__len__()?
Asked Answered
V

7

148

Is there any difference between calling len([1,2,3]) or [1,2,3].__len__()?

If there is no visible difference, what is done differently behind the scenes?

Venessavenetia answered 20/3, 2010 at 0:53 Comment(1)
See #496509Dorindadorine
T
148

len is a function to get the length of a collection. It works by calling an object's __len__ method. __something__ attributes are special and usually more than meets the eye, and generally should not be called directly.

It was decided at some point long ago getting the length of something should be a function and not a method code, reasoning that len(a)'s meaning would be clear to beginners but a.len() would not be as clear. When Python started __len__ didn't even exist and len was a special thing that worked with a few types of objects. Whether or not the situation this leaves us makes total sense, it's here to stay.

Titlark answered 20/3, 2010 at 0:57 Comment(1)
I'd argue having a universal standard across the language for getting an object's length, regardless of the data structure, is pretty awesome. Nice not having to remember if its obj.length, obj.length(), obj.size, obj.size(), or whatever else has been dreamt up over the yearsAnetta
A
92

It's often the case that the "typical" behavior of a built-in or operator is to call (with different and nicer syntax) suitable magic methods (ones with names like __whatever__) on the objects involved. Often the built-in or operator has "added value" (it's able to take different paths depending on the objects involved) -- in the case of len vs __len__, it's just a bit of sanity checking on the built-in that is missing from the magic method:

>>> class bah(object):
...   def __len__(self): return "an inch"
... 
>>> bah().__len__()
'an inch'
>>> len(bah())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object cannot be interpreted as an integer

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 <= sys.maxsize -- 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;-).

Other built-ins provide even more added value beyond simple sanity checks and readability. By uniformly designing all of Python to work via calls to builtins and use of operators, never through calls to magic methods, programmers are spared from the burden of remembering which case is which. (Sometimes an error slips in: until 2.5, you had to call foo.next() -- in 2.6, while that still works for backwards compatibility, you should call next(foo), and in 3.*, the magic method is correctly named __next__ instead of the "oops-ey" next!-).

So the general rule should be to never call a magic method directly (but always indirectly through a built-in) unless you know exactly why you need to do that (e.g., when you're overriding such a method in a subclass, if the subclass needs to defer to the superclass that must be done through explicit call to the magic method).

Avaavadavat answered 20/3, 2010 at 2:9 Comment(6)
I am beginner Python user (not the beginner programmer thought) and I am not sure about "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". I tried this: def len(x): return "I am a string." print(len(42)) print(len([1,2,3])) and it printed I am string twice. Can you explain it more?Giddens
@DarekNędza This has nothing to do with the above, which is about builtin len. You just defined your len function, that can of course return whatever you want. OP spoke about builtin len, which calls __len__ special method (not function) on the object under consideration.Ocher
@Ocher How can I be sure that I am calling built-in function len not some other function (like in my example) that happened to have same name - len. There is no warning like "You are redefining built-in function len" or something like this. In my opinion, I cannot be sure about what Alex stated in his answer.Giddens
Alex explicitly said if you're calling builtin, then you're sure..._. He said nothing about being sure you're calling builtin. But if you want to know that, you can: len in vars(__builtins__).values().Ocher
Sadly, this another example of the lack of a common base class for objects in Python. Syntactic context switching has always been nutty. In some cases it is common idiom to use an underscore method, in others one should use something like a function to do something common to many objects. It's also odd because many objects have no semantic use for len. Sometimes the object model is more C++ like, kitchen sinky..Nystatin
Don't get me wrong, I like using Python. I also like C++ but some parts of them are oddly not self-consistent.Nystatin
U
33

You can think of len() as being roughly equivalent to

def len(x):
    return x.__len__()

One advantage is that it allows you to write things like

somelist = [[1], [2, 3], [4, 5, 6]]
map(len, somelist) 

instead of

map(list.__len__, somelist)

or

map(operator.methodcaller('__len__'), somelist)

There is slightly different behaviour though. For example in the case of ints

>>> (1).__len__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__len__'
>>> len(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
Ultramarine answered 20/3, 2010 at 1:2 Comment(1)
I assume you mean operator.methodcaller instead of operator.attrgetter.Mathematician
A
5

You can check Pythond docs:

>>> class Meta(type):
...    def __getattribute__(*args):
...       print "Metaclass getattribute invoked"
...       return type.__getattribute__(*args)
...
>>> class C(object):
...     __metaclass__ = Meta
...     def __len__(self):
...         return 10
...     def __getattribute__(*args):
...         print "Class getattribute invoked"
...         return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__()                 # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c)          # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c)                      # Implicit lookup
10
Anderegg answered 18/2, 2015 at 9:23 Comment(0)
L
2

Well, len(s) is a built-in Python method which returns the length of an object. Now __len__() is a special method that is internally called by len(s) method to return the length of an object.

So, when we call len(s) method, s.__len__() is what actually happening behind the scenes to calculate the length.

The Python len() function can be interpreted as:

def len(s):
    return s.__len__()

reference

Lowestoft answered 16/5, 2021 at 7:20 Comment(0)
T
1

One additional comment:

len(c) and c.__len__() can return different values. This situation generally happens when we modify the __len__ function of the instance c.

>>> class C:
...     def __len__(self):
...         return 10
...
>>> c = C()
>>> c.__len__ = lambda: 3
>>> len(c)                    # C's __len__ invoked
10
>>> type(c).__len__(c)        # C's __len__ invoked
10
>>> c.__len__()               # c's __len__ invoked
3
Treacherous answered 27/4, 2023 at 17:2 Comment(0)
W
1

For built-in types, let's take list for instance. If you use help(list.__len__) to look up how list.__len__ works, it says:

>>> help(list.__len__)
Help on wrapper_descriptor:

__len__(self, /)
    Return len(self).

This indicates that for built-in data types, unlike the anwsers above, data_type.__len__() will call len(data_type), instead of the case that the latter calls the former.

Actually, if x is an instance of a built-in type, when you call len(x) CPython will read the length of the object directly from a C structure without calling any methods at all. Obtaining the number of elements in a collection is a very common operation. On types such as str, list, and memoryview, this operation must be efficient.

From Fluent Python, Luciano Ramalho

Please correct my words if I got them wrong.

Windup answered 5/8, 2023 at 8:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.