[Python-Dev] What is __int__ still useful for?

239 views
Skip to first unread message

Antoine Pitrou

unread,
Oct 13, 2021, 1:18:08 PM10/13/21
to pytho...@python.org

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 -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/K6TEYMDY5NEDV4MHH6EGIOQWDOAKSPJV/
Code of Conduct: http://python.org/psf/codeofconduct/

Serhiy Storchaka

unread,
Oct 13, 2021, 5:01:38 PM10/13/21
to pytho...@python.org
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__.

_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/IW4TTWZ2LGZOYUST2CA4X5HXM4XNWA72/

Greg Ewing

unread,
Oct 13, 2021, 6:26:47 PM10/13/21
to pytho...@python.org
On 14/10/21 6:10 am, Antoine Pitrou wrote:
> It seems that
> __int__ has now become a strict equivalent to __trunc__.

Not really -- __int__ is expected to return something of type
int, whereas __trunc__ is expected to return the same type as
its operand.

--
Greg
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/5XD26XS2WW7H2I3KZBY5EZLNV64RX6VM/

Greg Ewing

unread,
Oct 13, 2021, 6:59:10 PM10/13/21
to pytho...@python.org
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

_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/OM5UB5LRMOSKTVGJWV3G7TP7Z7DNFE2M/

Victor Stinner

unread,
Oct 13, 2021, 7:49:11 PM10/13/21
to Antoine Pitrou, Python Dev
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
--
Night gathers, and now my watch begins. It shall not end until my death.
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/QFMFPMCSDWV7S5QGPYS7K2VN4CHF5KDL/

Chris Angelico

unread,
Oct 13, 2021, 8:02:34 PM10/13/21
to Python Dev
On Thu, Oct 14, 2021 at 10:51 AM Victor Stinner <vsti...@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
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/WZSTHNTNZWHIKXWK2OOQK3MYI6PGPBJ5/

Guido van Rossum

unread,
Oct 13, 2021, 8:03:48 PM10/13/21
to Victor Stinner, Antoine Pitrou, Python Dev
On Wed, Oct 13, 2021 at 4:56 PM Victor Stinner <vsti...@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)

Antoine Pitrou

unread,
Oct 13, 2021, 8:10:51 PM10/13/21
to pytho...@python.org
On Thu, 14 Oct 2021 11:52:11 +1300
Greg Ewing <greg....@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.


_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/IRRRKKFXCS64LXXJF5TQPBF5MX4UOTIE/

Steven D'Aprano

unread,
Oct 13, 2021, 9:42:45 PM10/13/21
to pytho...@python.org
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
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/TFKQPKGUU3G2HI5AVIFFKSULU3YCLLRY/

Antoine Pitrou

unread,
Oct 14, 2021, 3:53:25 AM10/14/21
to pytho...@python.org
On Wed, 13 Oct 2021 17:00:49 -0700
Guido van Rossum <gu...@python.org> wrote:
> On Wed, Oct 13, 2021 at 4:56 PM Victor Stinner <vsti...@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.


_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/3BLOMBMUBTQMOZW7GBZS7DSP25MNOKUX/

Eryk Sun

unread,
Oct 14, 2021, 5:25:57 AM10/14/21
to pytho...@python.org
On 10/14/21, Antoine Pitrou <ant...@python.org> wrote:
> On Wed, 13 Oct 2021 17:00:49 -0700
> Guido van Rossum <gu...@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__().
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/Q77PFIMCHDGB36LZTNMFG6NF7DE2UOSF/

Antoine Pitrou

unread,
Oct 14, 2021, 5:36:46 AM10/14/21
to pytho...@python.org
On Thu, 14 Oct 2021 04:24:30 -0500
Eryk Sun <ery...@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.


_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/GL4XG2OW2ZZ7F45ILP4756WT5ALCVBOE/

Serhiy Storchaka

unread,
Oct 14, 2021, 6:52:05 AM10/14/21
to pytho...@python.org
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...@python.org/thread/5JKQMIC6EUVCD7IBWMRHY7DRTTNSBOWG/

_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/NU3774YDVCIUH44C7RZXCSSVRVYSLCUI/

Mark Dickinson

unread,
Oct 15, 2021, 8:16:57 AM10/15/21
to pytho...@python.org
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



Mark Dickinson

unread,
Oct 15, 2021, 8:22:46 AM10/15/21
to pytho...@python.org
Meta: apologies for failing to trim the context in the previous post.

-- 
Mark

Eryk Sun

unread,
Oct 15, 2021, 8:49:21 AM10/15/21
to pytho...@python.org
On 10/15/21, Mark Dickinson <dick...@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.
_______________________________________________
Python-Dev mailing list -- pytho...@python.org
To unsubscribe send an email to python-d...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/pytho...@python.org/message/R3JI6EGLMMBDNPCKFSNPLUNR2Q3ISAID/
Reply all
Reply to author
Forward
0 new messages