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".

Asked By: The Thonnu

||

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.

Answered By: chepner
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.