I've read somewhere that the use of ‘self’ in Python converts myobject.method (arg1, arg2)
into MyClass.method(myobject, arg1, arg2)
.
Does anyone know how I can prove this?
Is it only possible if I look at the bytecode by using dis.dis
?
I've read somewhere that the use of ‘self’ in Python converts myobject.method (arg1, arg2)
into MyClass.method(myobject, arg1, arg2)
.
Does anyone know how I can prove this?
Is it only possible if I look at the bytecode by using dis.dis
?
self doesn't do anything. self is merely the conventional name given to the first argument of a method in Python class definitions. This argument will be passed an instance of the class.
Essentially, to understand what is actually going on, you have to understand Python descriptors. The best places are the official docs
To boil it down, descriptor objects are objects that implement __get__
, __set__
or __delete__
. These methods intercept object attribute access, obj.x
, object attribute assignment: obj.x = 42
, and object attribute deletion, del obj.x
.
Also, check out the HOWTO, where they show how Python functions and methods are simply descriptors, and show an example Python implementation (of course, in CPython, this is implemented in C):
class Function(object):. . .def __get__(self, obj, objtype=None):"Simulate func_descr_get() in Objects/funcobject.c"if obj is None:return selfreturn types.MethodType(self, obj)
We can "cheat" and create our own object that merely wraps a function object, and see that this works.
import types
class Function:def __init__(self, func):self._func = funcdef __call__(self, *args, **kwargs):return self._func(*args, **kwargs)def __get__(self, obj, objtype=None):"Simulate func_descr_get() in Objects/funcobject.c https://docs.python.org/3/howto/descriptor.html#functions-and-methods"if obj is None:return selfelse:return types.MethodType(self, obj)class Foo:def __init__(self):self.foo = 42bar = Function(lambda self:self.foo ** 2
And or, in a REPL:
>>> import types
>>>
>>> class Function:
... def __init__(self, func):
... self._func = func
... def __call__(self, *args, **kwargs):
... return self._func(*args, **kwargs)
... def __get__(self, obj, objtype=None):
... "Simulate func_descr_get() in Objects/funcobject.c https://docs.python.org/3/howto/descriptor.html#functions-and-methods"
... if obj is None:
... return self
... else:
... return types.MethodType(self, obj)
...
>>> class Foo:
... def __init__(self):
... self.foo = 42
... bar = Function(lambda self:
... self.foo ** 2
... )
...
>>> Foo().bar()
1764
This shows you that the magic behind "self" is merely that function objects are descriptors, they implement a __get__
method which either returns the function itself if called without an instance, or returns a method-object that binds the first argument.