Two forms of functions

I am new to python. I see two kinds of functions. The first one is new to me,

a.append(x)

and the second one, which looks more familiar

a = len(x)

Can I conver the first form to the second form? Or vice versa?

This makes it sound like you used other programming languages before. Is that so? Which ones? This might make it easier to explain, in terms of what you already know.

I am used to matlab.

In

a.append(x)

a is an object. append is the method being called on the object. x is a parameter to the method.

This is a case of object oriented programming. The gist here is that instead of having a function like:

append(my_list, new_item)

that would need to be given all things each time its called, you have append(new_item) that is already aware of the list it is associated with (since it got that by virtue of being called via a.append(..).

So I can say:

a = [1,2,3]
a.append(4)
print(a)

and it would print:

[1, 2, 3, 4]

so it added 4 to the end of a.

len()

is a built-in function that gets the length (number of elements) of the given thing. Under the hood len() basically just calls the __len__() method of the given object.

So for our case:

a = [1,2,3]
print(len(a))
print(a.__len__())

prints:

3
3

We typically don’t call .__len__() as we generally would use len() here. (.__len__() is often an implementation detail).

I don’t really know the history of why len() is free-function instead of a normal method on the object.

2 Likes

A polymorphic helper-function

1 Like

It’s not difficult to look up:

The short version:

  • len can do validation that’s common to all types (i.e. ensure that an integer <= sys.maxsize is returned)
  • len can be optimized for primitives: at the C level, it can directly check a struct member (and avoid that validation step), whereas calling __len__ is emulated via a “slot-wrapper”
  • The operation makes sense for types that are basically unrelated, and isn’t gaining real benefit from polymorphism, so the usual OO reasons for preferring a method don’t apply
  • Perhaps most importantly :wink: it was GvR’s aesthetic/readability preference

Regarding optimization - for a user-defined class, __len__ is faster because it skips validation:

>>> import timeit
>>> class x:
...     def __len__(self): return 0
... 
>>> timeit.timeit("len(my_x)", setup="my_x=x()", globals=globals())
0.08995063393376768
>>> timeit.timeit("my_x.__len__", setup="my_x=x()", globals=globals())
0.038709620013833046

But for builtins it’s the other way around, because the __len__ is emulated in terms of primitive len:

>>> timeit.timeit("len('')")
0.03753247996792197
>>> timeit.timeit("''.__len__()")
0.06348034297116101
2 Likes