I have made a class called Emulate
. It takes an object and takes the attributes of the object.
class Emulate:
def __init__(self, obj):
for attr in dir(obj):
if attr != '__class__':
setattr(self, attr, getattr(obj, attr))
Now, I want to emulate the integer 1
:
one = Emulate(1)
However, I can't use /
when trying to divide:
print(one / 2)
gives TypeError: unsupported operand type(s) for /: 'Emulate' and 'int'
.
But, if I explicitly call __truediv__
:
print(one.__truediv__(2))
it works fine, giving 0.5
as the output.
What is happening here? I thought x / y
called x.__truediv__(y)
.
Note: this is not just for division. It is happening for all "magic methods".
CodePudding user response:
Magic methods are looked up on the class, not the individual object. Your Emulate
class does not define Emulate.__truediv__
, instead adding an instance attribute whose value is the method-wrapper
found on the int
value.
When you try to use one / 2
, the lookup of Emulate.__truediv__
fails.
When you try to use one.__truediv__
, the lookup of the instance attribute succeeds and produces a method wrapper that can be called with 2
as its argument, giving the observed result.
You can see this in the generated byte code:
>>> dis.dis('print(one/2)')
1 0 LOAD_NAME 0 (print)
2 LOAD_NAME 1 (one)
4 LOAD_CONST 0 (2)
6 BINARY_TRUE_DIVIDE
8 CALL_FUNCTION 1
10 RETURN_VALUE
Here, the opcode BINARY_TRUE_DIVIDE
is what takes care of trying to find the necessary method using the type of the value referenced by one
.
>>> dis.dis('print(one.__truediv__(2))')
1 0 LOAD_NAME 0 (print)
2 LOAD_NAME 1 (one)
4 LOAD_METHOD 2 (__truediv__)
6 LOAD_CONST 0 (2)
8 CALL_METHOD 1
10 CALL_FUNCTION 1
12 RETURN_VALUE
Here, an explicit method lookup on the object one
produces the value of the __truediv__
instance attribute you created.