Why is __truediv__ not getting called?
Question:
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".
Answers:
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.
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".
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.