Issue33039
Created on 2018-03-10 08:57 by ncoghlan, last changed 2022-04-11 14:58 by admin. This issue is now closed.
| Pull Requests | |||
|---|---|---|---|
| URL | Status | Linked | Edit |
| PR 13106 | closed | remi.lapeyre, 2019-05-06 14:02 | |
| Messages (10) | |||
|---|---|---|---|
| msg313515 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2018-03-10 08:57 | |
(Note: I haven't categorised this yet, as I'm not sure how it *should* be categorised)
Back when the __index__/nb_index slot was added, the focus was on allowing 3rd party integer types to be used in places where potentially lossy conversion with __int__/nb_int *wasn't* permitted.
However, this has led to an anomaly where the lossless conversion method *isn't* tried implicitly for the potentially lossy int() and math.trunc() calls, but is tried automatically in other contexts:
```
>>> import math
>>> class MyInt:
... def __index__(self):
... return 42
...
>>> int(MyInt())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string, a bytes-like object or a number, not 'MyInt'
>>> math.trunc(MyInt())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type MyInt doesn't define __trunc__ method
>>> hex(MyInt())
'0x2a'
>>> len("a" * MyInt())
42
```
Supporting int() requires also setting `__int__`:
```
>>> MyInt.__int__ = MyInt.__index__
>>> int(MyInt())
42
```
Supporting math.trunc() requires also setting `__trunc__`:
```
>>> MyInt.__trunc__ = MyInt.__index__
>>> math.trunc(MyInt())
42
```
(This anomaly was noticed by Eric Appelt while updating the int() docs to cover the fallback to trying __trunc__ when __int__ isn't defined: https://github.com/python/cpython/pull/6022#issuecomment-371695913)
|
|||
| msg313522 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2018-03-10 11:34 | |
Marking this as a documentation enhancement request for now, but I think we should also consider changing the type creation behaviour in 3.8 to implicitly add __int__ and __trunc__ definitions when __index__ is defined, but they aren't. That way, no behaviour will change for classes that explicitly define __int__ or __trunc__, but classes that only define __index__ without defining the other methods will behave more intuitively. |
|||
| msg332819 - (view) | Author: Rémi Lapeyre (remi.lapeyre) * | Date: 2018-12-31 14:52 | |
>I think we should also consider changing the type creation behaviour in 3.8 @ncoghlan is this what's being done in PyTypeReady? |
|||
| msg333546 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2019-01-13 07:47 | |
@Rémi Aye, filling out derived slots is one of the things PyType_Ready does. This would need a discussion on python-dev before going ahead and doing it though, as the closest equivalent we have to this right now is the "negative" derivation, where overriding __eq__ without overriding __hash__ implicitly marks the derived class as unhashable (look for "type->tp_hash = PyObject_HashNotImplemented;"). |
|||
| msg336044 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * ![]() |
Date: 2019-02-20 06:56 | |
See also issue33039. |
|||
| msg336201 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * ![]() |
Date: 2019-02-21 10:45 | |
Is not this a duplicate of issue20092? |
|||
| msg336234 - (view) | Author: Rémi Lapeyre (remi.lapeyre) * | Date: 2019-02-21 16:10 | |
Yes it is. Thanks for finding that @Serhiy. Since nobody objected to the change on the mailing list and people seem to agree in issue 20092: [R. David Murray] To summarize for anyone like me who didn't follow that issue: __index__ means the object can be losslessly converted to an int (is a true int), while __int__ may be an approximate conversion. Thus it makes sense for an object to have an __int__ but not __index__, but vice-versa does not make sense. I will post my patch tonight. |
|||
| msg340453 - (view) | Author: Cheryl Sabella (cheryl.sabella) * ![]() |
Date: 2019-04-17 22:43 | |
Rémi, Are you still working on the patch for this? Thanks! |
|||
| msg341505 - (view) | Author: Rémi Lapeyre (remi.lapeyre) * | Date: 2019-05-06 14:11 | |
Hi Cheryl, thanks for the ping. I wasn't sure my patch was correct but reading typeobject.c:add_operators(), it is actually more straight-forward than I thought. Serhiy Storchaka: This is indeed a duplicate of issue20092. I believe the solution proposed by Nick Coghlan is better than the one of Amitava Bhattacharyya, "adding a call to `nb_index` (if that slot exists) in `_PyLong_FromNbInt`" though. One thing to note regarding the proposed patch: the following stops to work and raises a RecursionError since __index__ == __int__: class MyInt(int): def __index__(self): return int(self) + 1 I changed test_int_subclass_with_index() as `int(self) + 1` is the same thing as `self + 1` for int subclasses. I don't think this sort of code should appear in the wild but if you think it is important not to break compatibility here, I think I could check for number subclasses before overriding __index__. |
|||
| msg341508 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * ![]() |
Date: 2019-05-06 14:29 | |
Then let to continue the discussion on the older issue which has larger discussion. |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022-04-11 14:58:58 | admin | set | github: 77220 |
| 2019-05-06 14:29:29 | serhiy.storchaka | set | status: open -> closed superseder: type() constructor should bind __int__ to __index__ when __index__ is defined and __int__ is not messages: + msg341508 resolution: duplicate |
| 2019-05-06 14:11:13 | remi.lapeyre | set | messages: + msg341505 |
| 2019-05-06 14:02:00 | remi.lapeyre | set | keywords:
+ patch stage: needs patch -> patch review pull_requests: + pull_request13020 |
| 2019-04-17 22:43:11 | cheryl.sabella | set | nosy:
+ cheryl.sabella messages: + msg340453 |
| 2019-02-21 16:10:40 | remi.lapeyre | set | messages: + msg336234 |
| 2019-02-21 10:45:51 | serhiy.storchaka | set | messages: + msg336201 |
| 2019-02-20 06:56:34 | serhiy.storchaka | set | nosy:
+ serhiy.storchaka messages: + msg336044 |
| 2019-01-13 07:47:32 | ncoghlan | set | messages: + msg333546 |
| 2018-12-31 14:52:24 | remi.lapeyre | set | nosy:
+ remi.lapeyre messages: + msg332819 |
| 2018-09-22 17:00:28 | xtreak | set | nosy:
+ xtreak |
| 2018-03-10 11:34:44 | ncoghlan | set | assignee: docs@python type: enhancement components: + Documentation versions: + Python 3.6, Python 3.7, Python 3.8 nosy: + docs@python messages:
+ msg313522 |
| 2018-03-10 08:57:11 | ncoghlan | create | |
