What is __int__ still useful for?

Hello, It used to be that defining __int__ allowed an object to be accepted as an integer from various functions, internal and third-party, thanks to being implicitly called by e.g. PyLong_AsLong. Today, and since bpo-37999, this is no longer the case. It seems that __int__ has now become a strict equivalent to __trunc__. Of course, user code can still look up and call the __int__ method explicitly (or look up the nb_int slot, in C), but that's a bit of anti-pattern. Is there a point in having three special methods __index__, __int__ and __trunc__, if two are them are practically interchangeable? Regards Antoine.

13.10.21 20:10, Antoine Pitrou пише:
It used to be that defining __int__ allowed an object to be accepted as an integer from various functions, internal and third-party, thanks to being implicitly called by e.g. PyLong_AsLong.
Today, and since bpo-37999, this is no longer the case. It seems that __int__ has now become a strict equivalent to __trunc__. Of course, user code can still look up and call the __int__ method explicitly (or look up the nb_int slot, in C), but that's a bit of anti-pattern.
Is there a point in having three special methods __index__, __int__ and __trunc__, if two are them are practically interchangeable?
Today __int__ allows the object be explicitly converted to int. It is defined for example in UUID and IPv4Address. We do not want them to be converted to int implicitly or be valid argument of math.trunc(). __trunc__ adds support of the type in math.trunc(). There is no requirement that it should return an int. GMP numbers can return GMP integers and NumPy arrays can return NumPy arrays (I do not know whether they do). It is similar to __floor__, __ceil__ and __round__.

On 14/10/21 11:19 am, Greg Ewing wrote:
Not really -- __int__ is expected to return something of type int, whereas __trunc__ is expected to return the same type as its operand.
Scratch that, it seems __trunc__ also returns an int, at least for floats. Not sure what the logic behind that is. There are differences between the functions int() and trunc() though:
int("42") 42
trunc("42") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: type str doesn't define __trunc__ method
Conceptually, I would say that int() is a type conversion, whereas trunc() is an operation on numbers. A type would be entitled to implement them both but differently. -- Greg

On Thu, 14 Oct 2021 11:52:11 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 14/10/21 11:19 am, Greg Ewing wrote:
Not really -- __int__ is expected to return something of type int, whereas __trunc__ is expected to return the same type as its operand.
Scratch that, it seems __trunc__ also returns an int, at least for floats. Not sure what the logic behind that is.
There are differences between the functions int() and trunc() though:
int("42") 42
trunc("42") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: type str doesn't define __trunc__ method
That is behind the point, because str doesn't define __int__ either. The int() function does more than just call __int__, it checks a whole lot of different possibilites (including checks for str and buffer-like objects, indeed). Similarly:
int(memoryview(b"123")) 123 memoryview(b"123").__int__() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'memoryview' object has no attribute '__int__'
Regards Antoine.

On Thu, Oct 14, 2021 at 11:52:11AM +1300, Greg Ewing wrote:
Scratch that, it seems __trunc__ also returns an int, at least for floats. Not sure what the logic behind that is.
I'm not sure about the logic either, but it is documented as returning an Integral: "Truncates the Real x to the nearest Integral toward 0." so the option is there for third-party types to return some integral type apart from int. For the stdlib, the only Integral type we have is int. So I think we have the following intended behaviour. * Round a numeric (Real) value to an Integral value: - round to nearest (ties to even): __round__ - round down (towards negative infinity): __floor__ - round up (towards positive infinity): __ceil__ - round towards zero: __trunc__ * Convert a numeric Integral value to an actual int: (intended for indexing of sequences): __index__ * Convert any arbitrary value to an actual int: __int__ Does that seem right? -- Steve

Hi Antoine, I have a lot of troubles to reminder how Python converts numbers, I collected notes about the Python "number tower" and the C implementation: https://pythondev.readthedocs.io/numbers.html Honestly, I don't understand well the difference between __int__() and __index__(). * https://docs.python.org/dev/reference/datamodel.html#object.__int__ * https://docs.python.org/dev/reference/datamodel.html#object.__index__ Victor On Wed, Oct 13, 2021 at 7:11 PM Antoine Pitrou <antoine@python.org> wrote:
Hello,
It used to be that defining __int__ allowed an object to be accepted as an integer from various functions, internal and third-party, thanks to being implicitly called by e.g. PyLong_AsLong.
Today, and since bpo-37999, this is no longer the case. It seems that __int__ has now become a strict equivalent to __trunc__. Of course, user code can still look up and call the __int__ method explicitly (or look up the nb_int slot, in C), but that's a bit of anti-pattern.
Is there a point in having three special methods __index__, __int__ and __trunc__, if two are them are practically interchangeable?
Regards
Antoine.
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/K6TEYMDY... Code of Conduct: http://python.org/psf/codeofconduct/
-- Night gathers, and now my watch begins. It shall not end until my death.

On Thu, Oct 14, 2021 at 10:51 AM Victor Stinner <vstinner@python.org> wrote:
Honestly, I don't understand well the difference between __int__() and __index__().
* https://docs.python.org/dev/reference/datamodel.html#object.__int__ * https://docs.python.org/dev/reference/datamodel.html#object.__index__
__int__ is for converting to integer, __index__ is for interpreting as integer. The intention of __index__ is that it already has that exact value, whereas __int__ might be rounding. For instance, int(5.25) is 5, but operator.index(5.25) raises. Quoting from that linked page on __index__: "Called ... whenever Python needs to **losslessly** convert the numeric object to an integer object" It is assumed to already be a numeric type (so "5" isn't, even though it could be cast to int), and the conversion should be lossless. I'm not 100% sure, but I think that, if __index__ returns anything, __int__ should return the same thing. There could be edge cases where that's not true though. ChrisA

On Wed, Oct 13, 2021 at 4:56 PM Victor Stinner <vstinner@python.org> wrote:
Honestly, I don't understand well the difference between __int__() and __index__().
* https://docs.python.org/dev/reference/datamodel.html#object.__int__ * https://docs.python.org/dev/reference/datamodel.html#object.__index__
If you want to index a list or array 'a' with index 'i', and i is not an int already, we try to convert it to int using __index__. This should fail for floats, since a[3.14] is a bug. OTOH, int(x) where x is a float should work, and that's where __int__ is used. And int(s) where s is a string should also work, so int() can't call __trunc__ (as was explained earlier in the thread). -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

On Wed, 13 Oct 2021 17:00:49 -0700 Guido van Rossum <guido@python.org> wrote:
On Wed, Oct 13, 2021 at 4:56 PM Victor Stinner <vstinner@python.org> wrote:
Honestly, I don't understand well the difference between __int__() and __index__().
* https://docs.python.org/dev/reference/datamodel.html#object.__int__ * https://docs.python.org/dev/reference/datamodel.html#object.__index__
If you want to index a list or array 'a' with index 'i', and i is not an int already, we try to convert it to int using __index__. This should fail for floats, since a[3.14] is a bug. OTOH, int(x) where x is a float should And int(s) where s is a string should also work, so int() can't call __trunc__ (as was explained earlier in the thread).
This seems like a red herring, because str.__int__ is not defined. The code to make int(str) work is a separate code path inside PyNumber_Long(). Note that PyNumber_Long() is now the only place inside the interpreter calling the `nb_int` slot. But since it also has those undesirable code paths accepting str and buffer-like objects, it's usable in fewer situations than you'd expect. Regards Antoine.

On 10/14/21, Antoine Pitrou <antoine@python.org> wrote:
On Wed, 13 Oct 2021 17:00:49 -0700 Guido van Rossum <guido@python.org> wrote:
so int() can't call __trunc__ (as was explained earlier in the thread).
I guess this was meant to be "*just* call __trunc__". It's documented that the int constructor calls the initializing object's __trunc__() method if the object doesn't implement __int__() or __index__().
Note that PyNumber_Long() is now the only place inside the interpreter calling the `nb_int` slot. But since it also has those undesirable code paths accepting str and buffer-like objects, it's usable in fewer situations than you'd expect.
Maybe an alternate constructor could be added -- such as int.from_number() -- which would be restricted to calling __int__(), __index__(), and __trunc__().

On Thu, 14 Oct 2021 04:24:30 -0500 Eryk Sun <eryksun@gmail.com> wrote:
Note that PyNumber_Long() is now the only place inside the interpreter calling the `nb_int` slot. But since it also has those undesirable code paths accepting str and buffer-like objects, it's usable in fewer situations than you'd expect.
Maybe an alternate constructor could be added -- such as int.from_number() -- which would be restricted to calling __int__(), __index__(), and __trunc__().
Perhaps. And ideally there would be a corresponding C API. Regards Antoine.

14.10.21 12:24, Eryk Sun пише:
Maybe an alternate constructor could be added -- such as int.from_number() -- which would be restricted to calling __int__(), __index__(), and __trunc__().
See thread "More alternate constructors for builtin type" on Python-ideas: https://mail.python.org/archives/list/python-ideas@python.org/thread/5JKQMIC...

I'd propose that we relegate `__trunc__` to the same status as `__floor__` and `__ceil__`: that is, have `__trunc__` limited to being support for `math.trunc`, and nothing more. Right now the `int` constructor potentially looks at all three of `__int__`, `__index__` and `__trunc__`, so the proposal would be to remove that special role of `__trunc__` and reduce the `int` constructor to only looking at `__int__` and `__index__`. Obviously that's a backwards incompatible change, but a fairly mild one, with an obvious place to insert a `DeprecationWarning` and a clear transition path for affected code: code that relies on `int` being able to use `__trunc__` would need to add a separate implementation of `__int__`. (We made this change recently for the `Fraction` type in https://bugs.python.org/issue44547.) I opened an issue for this proposal a few weeks back: https://bugs.python.org/issue44977 Mark On Thu, Oct 14, 2021 at 11:50 AM Serhiy Storchaka <storchaka@gmail.com> wrote:
14.10.21 12:24, Eryk Sun пише:
Maybe an alternate constructor could be added -- such as int.from_number() -- which would be restricted to calling __int__(), __index__(), and __trunc__().
See thread "More alternate constructors for builtin type" on Python-ideas:
https://mail.python.org/archives/list/python-ideas@python.org/thread/5JKQMIC...
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/NU3774YD... Code of Conduct: http://python.org/psf/codeofconduct/

On 10/15/21, Mark Dickinson <dickinsm@gmail.com> wrote:
the proposal would be to remove that special role of `__trunc__` and reduce the `int` constructor to only looking at `__int__` and `__index__`.
For Real and Rational numbers, currently the required method to implement is __trunc__(). ISTM that this proposal should include a change to require __int__() in numbers.Real.
participants (9)
-
Antoine Pitrou
-
Chris Angelico
-
Eryk Sun
-
Greg Ewing
-
Guido van Rossum
-
Mark Dickinson
-
Serhiy Storchaka
-
Steven D'Aprano
-
Victor Stinner