简体   繁体   中英

Not understanding why `__index__` method works

I'm working through this tutorial which has the following Binary class:

import collections

class Binary:
    def __init__(self, value=0):
        if isinstance(value, collections.Sequence):
            if len(value) > 2 and value[0:2] == '0b':
                self._value = int(value, base=2)
            elif len(value) > 2 and value[0:2] == '0x':
                self._value = int(value, base=16)
            else:
                self._value = int(''.join([str(i) for i in value]), base=2)
        else:
            try:
                self._value = int(value)
                if self._value < 0:
                    raise ValueError("Binary cannot accept negative numbers. Use SizedBinary instead")
            except ValueError:
                raise ValueError("Cannot convert value {} to Binary".format(value))

    def __int__(self):
        return self._value

when running a unittest with pytest we get:

    def test_binary_hex():
        binary = Binary(6)
>       assert hex(binary) == '0x6'
E       TypeError: 'Binary' object cannot be interpreted as an integer

To solve this , the article states:

According to the official documentation, "If x is not a Python int object, it has to define an __index__() method that returns an integer." so this is what we are missing. As for __index__() , the documentation states that "In order to have a coherent integer type class, when __index__() is defined __int__() should also be defined, and both should return the same value."

So we just have to add

def __index__(self):
    return self.__int__()

This does work, but why do the __index__ and __int__ as written here, return the same value? Regarding:

def __index__(self):
    return self.__int__()

Shouldnt it be : return self._value. __int__()

edit:

with:

    def __int__(self):
        return self._value

This makes sense to me because _value is an integer.

with:

def __index__(self):
    return self.__int__()

In this case again self is an instance of a class , which in theory could many attributes (although not in this case). I don't understand how in return self. __int__() , the __int__() knows to get the integer stored in _value if we don't pass _value explicitly to __int__()

self._value is an int, calling the __int__() method on an int returns the same value, so writing:

return self._value.__int__()

would do the same as:

return self._value

And just to prove my point it would be the same as:

return int(int(int(int(self._value))))

In most cases you can write __index__ to be a direct alias to the same function:

def __int__(self):
    return self._value
__index__ = __int__

PS You may want to look at PEP 357 for the rational of why the __index__ method exists.

Basically it is to distinguish between objects that can be converted to an int and objects that are inherently integers:

>>> int(1.3)
1
>>> hex(1.3)
Traceback (most recent call last):
  File "<pyshell#55>", line 1, in <module>
    hex(1.3)
TypeError: 'float' object cannot be interpreted as an integer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM