How does Python say “this is called a function”?

The intended object should be like this, defining __call__ . The class must be an object ... or at least with some exceptions. This exception is something that I cannot formally clarify, so this question is posted here.

Let A be a simple class:

 class A(object): def call(*args): return "In `call`" def __call__(*args): return "In `__call__`" 

The first function is intentionally called a “call” to clearly indicate whether the comparison is with another.

Let me create an instance and forget about the expression, which it implies:

 a = A() # Think of it as `a = magic` and forget about `A()` 

Now what is worth:

 print(A.call()) print(a.call()) print(A()) print(a()) 

Result:

 >>> In `call` >>> In `call` >>> <__main__.A object at 0xNNNNNNNN> >>> In `__call__` 

The output (the third statement does not work __call__ ) is not a surprise, but when I think that everyone that says "Python class are objects" ...

This is more explicit, however run __call__

 print(A.__call__()) print(a.__call__()) >>> "In `__call__`" >>> "In `__call__`" 

All this is just to show how, finally, A() may seem strange.

There are exceptions to the Python rules, but the documentation on the “object. Call doesn't say much about __call__ ... nothing more:

3.3.5. Emulating Called Objects

 object.__call__(self[, args...]) 

Called when an instance is "called" as a function; [...]

But how does Python say "it is called as a function" and does this rule honor object.__call__ ?

It may be a type question, but even a type has an object as a base class.

Where can I find out more (and formally) about this?

By the way, is there any difference between Python2 and Python3?

-----% <----- edit ----->% -----

Conclusions and other experiments after one answer and one comment

Update # 1

After @Veedrac's answer and @chepner's comment, I came to this other test that fills in the comments of both:

 class M(type): def __call__(*args): return "In `M.__call__`" class A(object, metaclass=M): def call(*args): return "In `call`" def __call__(*args): return "In `A.__call__`" print(A()) 

Result:

 >>> In `M.__call__` 

So it seems like a meta class that manages the "call" operations. If I understand correctly, a metaclass does not matter only for the class, but also for class instances.

Update # 2

Another relevant test that shows this is not an attribute of the object that matters, but an attribute of the type of object:

 class A(object): def __call__(*args): return "In `A.__call__`" def call2(*args): return "In `call2`" a = A() print(a()) 

As expected, it prints:

 >>> In `A.__call__` 

Now this:

 a.__call__ = call2 print(a()) 

He prints:

 >>> In `A.__call__` 

Same thing before the attribute. It does not print In call2 , it is still In A.__call__ . It is important to note, as well as explain why the __call__ metaclass that was called (remember that the metaclass is a type of class object). __call__ , used to call a function, not from an object, but from its type.

+5
source share
1 answer

x(*args, **kwargs) same as type(x).__call__(x, *args, **kwargs) .

So you have

 >>> type(A).__call__(A) <__main__.A object at 0x7f4d88245b50> 

and it all makes sense.


chepner indicates in the comments that type(A) == type . This is kind of wierd, because type(A)(A) just gives type again! But remember that instead we use type(A).__call__(A) , which is not the same .

So this resolves type.__call__(A) . This is a constructor function for classes that builds data structures and performs all the construction magic.


The same applies to most dunder (double underscore) methods, such as __eq__ . This is partly an optimization in these cases.

+5
source

Source: https://habr.com/ru/post/1202263/


All Articles