Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python bytecode function call passing self

I'm trying to understand how bytecode works.

a.func() is a function call. The corresponding bytecode is roughly LOAD_GLOBAL a, LOAD_ATTR attr and then CALL_FUNCTION with 0 arguments.

This is totally fine if a is a module. But if a is an object, it has to pass the object instance itself. Since Python could NOT know whether a is a module or an object at compile time, naturally the bytecode is same regardless of the type of a. But how does the runtime system handle self as the first argument to func if a is an object? Is there some special handling below bytecode level that says "if it is called on an object prepend the object as the first argument"?

like image 341
Hot.PxL Avatar asked Oct 17 '25 07:10

Hot.PxL


2 Answers

The bytecode doesn't have to vary for different object types. It is the responsibility of the object type itself to manage binding behaviour. This is covered in the descriptor protocol.

In short, LOAD_ATTR delegates attribute access to the object, via the object.__getattribute__ hook:

Called unconditionally to implement attribute accesses for instances of the class.

For modules, __getattribute__ simply looks up the name in the __dict__ namespace and returns it. But for classes and metaclasses, the implementation will invoke the descriptor protocol if the attribute supports this. Functions support the descriptor protocol and return a bound method when so asked:

>>> class Foo:
...     def method(self): pass
...
>>> Foo().method  # access on an instance -> binding behaviour
<bound method Foo.method of <__main__.Foo object at 0x107155828>>
>>> Foo.method    # access on the class, functions just return self when bound here
<function Foo.method at 0x1073702f0>
>>> Foo.method.__get__(Foo(), Foo)  # manually bind the function
<bound method Foo.method of <__main__.Foo object at 0x107166da0>>

This binding behaviour also underlies how property, classmethod and staticmethod objects work (the latter neuters the binding behaviour of a function by returning the function itself).

like image 113
Martijn Pieters Avatar answered Oct 22 '25 06:10

Martijn Pieters


LOAD_ATTR does the magic via descriptors ( https://docs.python.org/2/howto/descriptor.html ).

Assuming a is object of class A: In python functions are descriptors. When you do a.func, in reality it returns A.func, which is descriptor object (unbound function). It then "upgrades" itself to bound function (A.func.__get__ is called). Unbound function must be given self argument as first explicitly. Bound function already has self argument remembered "inside" itself.

In python module is an object and uses exactly the same mechanism.

like image 39
Radosław Cybulski Avatar answered Oct 22 '25 06:10

Radosław Cybulski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!