Semantics of __int__(), __index__()
Hi all, I was looking into http://bugs.python.org/issue17576 and I found that the semantics of __int__() and __index__() are not precisely defined in the documentation and that the implementation (CPython 3.4a) has some odd behaviour. Defining two classes: class Int1(int): def __init__(self, val=0): print("new %s" % self.__class__) class Int2(Int1): def __int__(self): return self and two instances i1 = Int1() i2 = Int2() we get the following behaviour:
type(int(i1))
I would have expected 'Int1'
type(int(i2)) new
Why is a new Int2 being created? operator.index does similar things. So, 1. Should type(int(x)) be exactly int, or is any subclass OK? 2. Should type(index(x)) be exactly int, or is any subclass OK? 3. Should int(x) be defined as int_check(x.__int__())? 4. Should operator.index(x) be defined as index_check(x.__index__())? where: def int_check(x): if is_int(x): return x else: raise TypeError(...) def index_check(x): if is_index(x): return x else: raise TypeError(...) The definition of is_int(x) and is_index(x) follow from the answers to 1 and 2. I had previously assumed (and would expect) that the answers were: 1. Any subclass is OK 2. Ditto 3. Yes 4. Yes Which means that def is_int(x): return int in type(x).__mro__ is_index = is_int Cheers, Mark.
On Sun, Mar 31, 2013 at 2:29 PM, Mark Shannon wrote:
class Int1(int): def __init__(self, val=0): print("new %s" % self.__class__)
class Int2(Int1): def __int__(self): return self
and two instances i1 = Int1() i2 = Int2()
we get the following behaviour:
type(int(i1))
I would have expected 'Int1'
Wouldn't that remove the one obvious way to get an 'int' from an 'Int1'?
1. Should type(int(x)) be exactly int, or is any subclass OK? 2. Should type(index(x)) be exactly int, or is any subclass OK? 3. Should int(x) be defined as int_check(x.__int__())? 4. Should operator.index(x) be defined as index_check(x.__index__())?
For (1), I'd say yes, it should be exactly an int, so my answer to (3) is no. As written, int_check would do the wrong thing for bools, too: I definitely want int(True) to be 1, not True. For (2) and (4), it's not so clear. Are there use-cases for an __index__ return value that's not directly of type int? I can't think of any offhand. Mark
On Mon, Apr 1, 2013 at 12:28 AM, Mark Dickinson
As written, int_check would do the wrong thing for bools, too: I definitely want int(True) to be 1, not True.
For (2) and (4), it's not so clear. Are there use-cases for an __index__ return value that's not directly of type int? I can't think of any offhand.
for base in (str, bytes, bytearray, int, float, complex, dict, tuple,
int() and operator.index() are both type coercion calls to produce true
Python integers - they will never return a subclass, and this is both
deliberate and consistent with all the other builtin types that accept an
instance of themselves as input to the constructor. Passing a subclass
instance to the base class constructor is the way you convert a subclass to
an ordinary instance of the base class:
list, set, frozenset):
... class subclass(base): pass
... print("'type(base(subclass()))' is", type(base(subclass())))
...
'type(base(subclass()))' is
On Tue, Apr 2, 2013 at 1:44 AM, Nick Coghlan
int() and operator.index() are both type coercion calls to produce true Python integers - they will never return a subclass, and this is both deliberate and consistent with all the other builtin types that accept an instance of themselves as input to the constructor.
That's good to hear.
There's code in the slot wrappers so that if you return a non-int object from either __int__ or __index__, then the interpreter will complain about it, and if you return a subclass, it will be stripped back to just the base class.
Can you point me to that code? All I could find was PyLong_Check calls (I was looking for PyLong_CheckExact). Mark
On Tue, Apr 2, 2013 at 8:07 AM, Mark Dickinson
On Tue, Apr 2, 2013 at 1:44 AM, Nick Coghlan
wrote: There's code in the slot wrappers so that if you return a non-int object from either __int__ or __index__, then the interpreter will complain about it, and if you return a subclass, it will be stripped back to just the base class.
Can you point me to that code? All I could find was PyLong_Check calls (I was looking for PyLong_CheckExact).
And indeed: iwasawa:Objects mdickinson$ /opt/local/bin/python3.3 Python 3.3.0 (default, Sep 29 2012, 08:16:19) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin Type "help", "copyright", "credits" or "license" for more information.
class A: ... def __int__(self): ... return True ... def __index__(self): ... return False ... a = A() int(a) True import operator; operator.index(a) False
Which means I have to do int(int(a)) to get the actual integer value. Grr. Mark
On 02/04/13 01:44, Nick Coghlan wrote:
On Mon, Apr 1, 2013 at 12:28 AM, Mark Dickinson
mailto:dickinsm@gmail.com> wrote: As written, int_check would do the wrong thing for bools, too: I definitely want int(True) to be 1, not True.
For (2) and (4), it's not so clear. Are there use-cases for an __index__ return value that's not directly of type int? I can't think of any offhand.
int() and operator.index() are both type coercion calls to produce true Python integers - they will never return a subclass, and this is both deliberate and consistent with all the other builtin types that accept an instance of themselves as input to the constructor. Passing a subclass instance to the base class constructor is the way you convert a subclass to an ordinary instance of the base class:
Unfortunately, that is not true :(
class Int(int): ... def __int__(self): ... return self ... type(int(Int()))
Hence my original question: what *should* the semantics be?
for base in (str, bytes, bytearray, int, float, complex, dict, tuple, list, set, frozenset): ... class subclass(base): pass ... print("'type(base(subclass()))' is", type(base(subclass()))) ... 'type(base(subclass()))' is
'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is 'type(base(subclass()))' is There's code in the slot wrappers so that if you return a non-int object from either __int__ or __index__, then the interpreter will complain about it, and if you return a subclass, it will be stripped back to just the base class.
If the language and library reference aren't clear on this, it's a documentation issue.
Cheers, Nick.
-- Nick Coghlan | ncoghlan@gmail.com mailto:ncoghlan@gmail.com | Brisbane, Australia
On Tue, Apr 2, 2013 at 9:33 AM, Mark Shannon wrote:
Hence my original question: what *should* the semantics be?
I like Nick's answer to that: int *should* always return something of exact type int. Otherwise you're always left wondering whether you have to do "int(int(x))", or perhaps even "int(int(int(x)))", to be absolutely sure of getting an int. The question is whether / how to fix the current behaviour, given that it doesn't conform to those ideal semantics. Mark
On Tue, Apr 2, 2013 at 10:53 AM, Mark Dickinson
On Tue, Apr 2, 2013 at 9:33 AM, Mark Shannon wrote:
Hence my original question: what *should* the semantics be?
I like Nick's answer to that: int *should* always return something of exact type int. Otherwise you're always left wondering whether you have to do "int(int(x))", or perhaps even "int(int(int(x)))", to be absolutely sure of getting an int.
The question is whether / how to fix the current behaviour, given that it doesn't conform to those ideal semantics.
Mark
My 2 cents here is that which one is called seems to be truly random. Try looking into what builtin functions call (for example list.pop calls __int__, who knew)
On Tue, Apr 2, 2013 at 9:58 AM, Maciej Fijalkowski
My 2 cents here is that which one is called seems to be truly random. Try looking into what builtin functions call (for example list.pop calls __int__, who knew)
That sounds like a clear bug to me. It should definitely be using __index__. Mark
On Tue, Apr 2, 2013 at 10:02 AM, Mark Dickinson
On Tue, Apr 2, 2013 at 9:58 AM, Maciej Fijalkowski
wrote: My 2 cents here is that which one is called seems to be truly random. Try looking into what builtin functions call (for example list.pop calls __int__, who knew)
That sounds like a clear bug to me. It should definitely be using __index__.
Ah, and I see it *is* using `__index__` in Python 3; just not in Python 2.7. It may be one of those Python 2 bugs that's not worth fixing because the fix would do more harm (in the form of breakage of existing code) than good. -- Mark
Le Tue, 2 Apr 2013 09:53:41 +0100,
Mark Dickinson
On Tue, Apr 2, 2013 at 9:33 AM, Mark Shannon wrote:
Hence my original question: what *should* the semantics be?
I like Nick's answer to that: int *should* always return something of exact type int. Otherwise you're always left wondering whether you have to do "int(int(x))", or perhaps even "int(int(int(x)))", to be absolutely sure of getting an int.
Agreed. Antoine.
On 2 Apr 2013 19:04, "Antoine Pitrou"
Le Tue, 2 Apr 2013 09:53:41 +0100, Mark Dickinson
a écrit : On Tue, Apr 2, 2013 at 9:33 AM, Mark Shannon wrote:
Hence my original question: what *should* the semantics be?
I like Nick's answer to that: int *should* always return something of exact type int. Otherwise you're always left wondering whether you have to do "int(int(x))", or perhaps even "int(int(int(x)))", to be absolutely sure of getting an int.
Agreed.
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4? (I like the idea of an explicit error over implicit conversion to the base type, so deprecation of subtypes makes sense as a way forward. We should check the other type coercion methods, too.) Cheers, Nick.
Antoine.
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe:
http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com
On Wed, Apr 3, 2013 at 12:17 PM, Nick Coghlan
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4?
+1. Sounds good to me.
(I like the idea of an explicit error over implicit conversion to the base type, so deprecation of subtypes makes sense as a way forward. We should check the other type coercion methods, too.)
Agreed on both points. Mark
On 04/03/2013 01:17 PM, Nick Coghlan wrote:
I like Nick's answer to that: int *should* always return something of exact type int. Otherwise you're always left wondering whether you have to do "int(int(x))", or perhaps even "int(int(int(x)))", to be absolutely sure of getting an int.
Agreed.
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4?
Why would one want to be absolutely sure of getting an int? It seems like a good feature that an __int__ implementation can choose to return an int subclass with additional (and optional) information. After all, int subclass instances should be usable everywhere where ints are, including in C code. I can imagine numpy and similar projects would be making use of this ability already -- just think of uses for numpy's subclasses of "float". If one wants to break the abstraction and be absolutely positively sure of getting an int and not a subclass thereof, they can write something like (0).__add__(obj). But I suspect this will be extremely rare.
On 2013-04-03 13:47, Hrvoje Niksic wrote:
On 04/03/2013 01:17 PM, Nick Coghlan wrote:
I like Nick's answer to that: int *should* always return something of exact type int. Otherwise you're always left wondering whether you have to do "int(int(x))", or perhaps even "int(int(int(x)))", to be absolutely sure of getting an int.
Agreed.
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4?
Why would one want to be absolutely sure of getting an int?
It seems like a good feature that an __int__ implementation can choose to return an int subclass with additional (and optional) information. After all, int subclass instances should be usable everywhere where ints are, including in C code. I can imagine numpy and similar projects would be making use of this ability already -- just think of uses for numpy's subclasses of "float".
We don't. [~] |1> type(float(np.float64(1.0))) float [~] |2> type(int(np.int32(1))) int -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
On 03/04/13 23:47, Hrvoje Niksic wrote:
On 04/03/2013 01:17 PM, Nick Coghlan wrote:
I like Nick's answer to that: int *should* always return something of exact type int. Otherwise you're always left wondering whether you have to do "int(int(x))", or perhaps even "int(int(int(x)))", to be absolutely sure of getting an int.
Agreed.
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4?
Why would one want to be absolutely sure of getting an int?
It seems like a good feature that an __int__ implementation can choose to return an int subclass with additional (and optional) information. After all, int subclass instances should be usable everywhere where ints are, including in C code.
I agree with Hrvoje here, and I have code that's probably going to be impacted by any change in behaviour.
In OO terms, an instance of an int subclass *is* an int, and we shouldn't care whether __int__ returns a subclass or a builtin. I think that before any change is made, even mere DeprecationWarning, there needs to be a very strong reason justifying restricting __int__ to return a built-in int.
To put it another way, I think it is perfectly reasonable for __int__ to enforce the constraint ``isinstance(result, int)`` but *not* to enforce the constraint ``type(result) is int``. We rarely, if ever, write explicit type checks like the second. Why should __int__ etc. implicitly do so?
This issue doesn't just apply to ints. For example:
py> class MyStr(str):
... def __str__(self):
... return self
...
py> s = MyStr('spam')
py> type(str(s))
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 04/03/2013 11:49 AM, Steven D'Aprano wrote:
-1 on forcing __int__, __str__, __float__ etc. to return instances of the built-in types.
- -1 as well, for the reasons Steven lists. The only quasi-legitimate reason I know of for checking 'type(x) is int' rather than 'isinstance(x, int)' is speed. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with undefined - http://www.enigmail.net/ iEYEARECAAYFAlFcV/wACgkQ+gerLs4ltQ6OBwCg0YMyUdiji82TwYQZTx85F9cJ wmMAoKBL13C+a4MN640jL5X+X+G9RP5b =8q3C -----END PGP SIGNATURE-----
On 03Apr2013 14:47, Hrvoje Niksic
On 04/04/13 09:07, Cameron Simpson wrote:
On 03Apr2013 14:47, Hrvoje Niksic
wrote: | On 04/03/2013 01:17 PM, Nick Coghlan wrote: | Why would one want to be absolutely sure of getting an int? So that arithmetic can be relied upon? If a subclass can override the add methods etc it can look like an int, be a subclass instance of an int, and yet not act like an int in all ways.
Python is generally a consenting adults language. If you don't trust the subclass to actually behave like an int in ways that matter, why are you using it? Ultimately, something may have monkey-patched builtins int without your knowledge, so even calling int() doesn't guarantee anything about the object you get back. Isn't it funny how we have differences in opinion on just when and how we're allowed to shoot ourselves in the foot? I personally hate the idea that global constants aren't constant, and can be rebound by anything, but others apparently don't care. You hate the idea that int() might return an instance of a subclass, and I think that's a feature, not a bug. -- Steven
On Wed, Apr 3, 2013 at 6:47 AM, Hrvoje Niksic
It seems like a good feature that an __int__ implementation can choose to return an int subclass with additional (and optional) information. After all, int subclass instances should be usable everywhere where ints are, including in C code.
Unless you want to try to use the concrete C-API in CPython. In my experience the concrete API is not very subclass friendly. -eric
On 4 Apr 2013 14:58, "Eric Snow"
On Wed, Apr 3, 2013 at 6:47 AM, Hrvoje Niksic
wrote:
It seems like a good feature that an __int__ implementation can choose to return an int subclass with additional (and optional) information. After all, int subclass instances should be usable everywhere where ints are, including in C code.
Unless you want to try to use the concrete C-API in CPython. In my experience the concrete API is not very subclass friendly.
Using it with subclasses is an outright bug (except as part of a subclass implementation). Cheers, Nick.
-eric _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe:
http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com
Eric Snow:
On Wed, Apr 3, 2013 at 6:47 AM, Hrvoje Niksic
wrote: It seems like a good feature that an __int__ implementation can choose to return an int subclass with additional (and optional) information. After all, int subclass instances should be usable everywhere where ints are, including in C code.
Unless you want to try to use the concrete C-API in CPython. In my experience the concrete API is not very subclass friendly.
Nick:
Using it with subclasses is an outright bug (except as part of a subclass implementation).
This is true for mutable objects like dicts and lists where calling things like PyDict_SetItem will happily circumvent the object. But for ints and floats, all that the C code really cares about is the object's intrinsic value as returned by PyLong_AS_LONG and friends, which is constant and unchangeable by subclasses. The typical reason to subclass int is to add more information or new methods on the instance, not to break basic arithmetic. Doing anything else breaks subtitutability and is well outside the realm of "consenting adults". Someone who wants to change basic arithmetic is free to implement an independent int type. Hrvoje
On 4 April 2013 10:39, Hrvoje Niksic
On Wed, Apr 3, 2013 at 6:47 AM, Hrvoje Niksic
wrote: It seems like a good feature that an __int__ implementation can choose to return an int subclass with additional (and optional) information. After all, int subclass instances should be usable everywhere where ints are, including in C code.
[SNIP]
The typical reason to subclass int is to add more information or new methods on the instance, not to break basic arithmetic. Doing anything else breaks subtitutability and is well outside the realm of "consenting adults". Someone who wants to change basic arithmetic is free to implement an independent int type.
The reason for calling int(obj) is to get an object that is precisely of type int. When I call this I do not want any modified or additional methods or data attached to the resulting object. The example given at the start of the thread makes minimal modifications in an int subclass but still allows the result of int(obj) to be unpickleable:
class Int1(int): ... def __int__(self): ... return self ... n = Int1(4) n 4 import pickle ni = int(n) pickle.dumps(ni) ... snip ... File "q:\tools\Python27\lib\pickle.py", line 562, in save_tuple save(element) File "q:\tools\Python27\lib\pickle.py", line 331, in save self.save_reduce(obj=obj, *rv) File "q:\tools\Python27\lib\pickle.py", line 401, in save_reduce save(args) File "q:\tools\Python27\lib\pickle.py", line 286, in save f(self, obj) # Call unbound method with explicit self File "q:\tools\Python27\lib\pickle.py", line 562, in save_tuple save(element) File "q:\tools\Python27\lib\pickle.py", line 291, in save issc = issubclass(t, TypeType) RuntimeError: maximum recursion depth exceeded while calling a Python object
I don't know whether that's a bug in pickle, but I think it's fair to say that there are times when someone wants an object that is precisely of type int. They should be able to rely on int(obj) returning an int or raising an error. This is true similarly for __index__. The entire purpose of __index__ is to permit APIs like list.__getitem__ to work extensibly with any int-like object. This is achieved by allowing any object to advertise its convertibility from an int-like object to an int with the same numeric value. Anyone who calls operator.index(obj) is explicitly stating that they do not want any property of obj apart from its integer value and that they want that as an int. That it should be an object of type int is explicitly stated in PEP 357 (http://www.python.org/dev/peps/pep-0357/): """ 2) The __index__ special method will have the signature def __index__(self): return obj where obj must be either an int or a long. """ Oscar
On Fri, Apr 5, 2013 at 1:23 AM, Oscar Benjamin
The reason for calling int(obj) is to get an object that is precisely of type int. When I call this I do not want any modified or additional methods or data attached to the resulting object.
There's something I'm fundamentally not understanding about this debate, and that is: How is it that calling a class can logically return anything other than an instance of that class? Taking it to a user-defined type: class Foo: pass class Bar(Foo): pass Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here? ChrisA
Am 04.04.2013 16:47, schrieb Chris Angelico:
On Fri, Apr 5, 2013 at 1:23 AM, Oscar Benjamin
wrote: The reason for calling int(obj) is to get an object that is precisely of type int. When I call this I do not want any modified or additional methods or data attached to the resulting object.
There's something I'm fundamentally not understanding about this debate, and that is: How is it that calling a class can logically return anything other than an instance of that class? Taking it to a user-defined type:
class Foo: pass
class Bar(Foo): pass
Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
I think the issue that the constructors for basic classes like int() are often seen as built-in functions that "cast" values to the respective type (or alternatively, functions that call the respective __method__ on the argument, like len()), not class constructors. FWIW, I agree with you that the "class constructor" view is the right one and would prefer exact int/str/... instances returned. Georg
On Thu, Apr 4, 2013 at 7:47 AM, Chris Angelico
On Fri, Apr 5, 2013 at 1:23 AM, Oscar Benjamin
wrote: The reason for calling int(obj) is to get an object that is precisely of type int. When I call this I do not want any modified or additional methods or data attached to the resulting object.
There's something I'm fundamentally not understanding about this debate, and that is: How is it that calling a class can logically return anything other than an instance of that class? Taking it to a user-defined type:
class Foo: pass
class Bar(Foo): pass
Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
A class can define a __new__ method that returns a different object. E.g. (python 3):
class C: ... def __new__(cls): return 42 ... C() 42
-- --Guido van Rossum (python.org/~guido)
On Fri, Apr 5, 2013 at 1:59 AM, Guido van Rossum
On Thu, Apr 4, 2013 at 7:47 AM, Chris Angelico
wrote: Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
A class can define a __new__ method that returns a different object. E.g. (python 3):
Right, I'm aware it's possible. But who would expect it of a class? ChrisA
On 2013-04-04, at 17:01 , Chris Angelico wrote:
On Fri, Apr 5, 2013 at 1:59 AM, Guido van Rossum
wrote: On Thu, Apr 4, 2013 at 7:47 AM, Chris Angelico
wrote: Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
A class can define a __new__ method that returns a different object. E.g. (python 3):
Right, I'm aware it's possible. But who would expect it of a class?
Given it's one of the use cases for __new__ and immutable types have to be initialized through __new__ anyway, why would it be unexpected?
On 04/04/2013 08:01 AM, Chris Angelico wrote:
On Fri, Apr 5, 2013 at 1:59 AM, Guido van Rossum
wrote: On Thu, Apr 4, 2013 at 7:47 AM, Chris Angelico
wrote: Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
A class can define a __new__ method that returns a different object. E.g. (python 3):
Right, I'm aware it's possible. But who would expect it of a class?
FTR I'm in the int() should return an int camp, but to answer your question: my dbf module has a Table class, but it returns either a Db3Table, FpTable, VfpTable, or ClpTable depending on arguments (if creating a new one) or the type of the table in the existing dbf file. -- ~Ethan~
On 5 April 2013 02:16, Ethan Furman
On 04/04/2013 08:01 AM, Chris Angelico wrote:
On Fri, Apr 5, 2013 at 1:59 AM, Guido van Rossum
wrote: On Thu, Apr 4, 2013 at 7:47 AM, Chris Angelico
wrote: Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
A class can define a __new__ method that returns a different object. E.g. (python 3):
Right, I'm aware it's possible. But who would expect it of a class?
FTR I'm in the int() should return an int camp, but to answer your question: my dbf module has a Table class, but it returns either a Db3Table, FpTable, VfpTable, or ClpTable depending on arguments (if creating a new one) or the type of the table in the existing dbf file.
I fall into: 1. int(), float(), str() etc should return that exact class (and operator.index() should return exactly an int). 2. It could sometimes be useful for __int__() and __index__() to return a subclass of int. So, for the int constructor, I would have the following logic (assume appropriate try/catch): def __new__(cls, obj): i = obj.__int__() if type(i) is int: return i return i._internal_value Tim Delaney
On Thu, Apr 4, 2013 at 1:50 PM, Tim Delaney
I fall into:
1. int(), float(), str() etc should return that exact class (and operator.index() should return exactly an int).
2. It could sometimes be useful for __int__() and __index__() to return a subclass of int.
So, for the int constructor, I would have the following logic (assume appropriate try/catch):
def __new__(cls, obj): i = obj.__int__()
if type(i) is int: return i
return i._internal_value
CPython can solve this in C using an unsafe cast, and the code that checks for allowable subclasses of int actually ensures such a cast will work. But it still feels wrong; __int__ should be expected to do the work. -- --Guido van Rossum (python.org/~guido)
Guido van Rossum, 04.04.2013 23:14:
On Thu, Apr 4, 2013 at 1:50 PM, Tim Delaney wrote:
I fall into:
1. int(), float(), str() etc should return that exact class (and operator.index() should return exactly an int).
2. It could sometimes be useful for __int__() and __index__() to return a subclass of int.
So, for the int constructor, I would have the following logic (assume appropriate try/catch):
def __new__(cls, obj): i = obj.__int__()
if type(i) is int: return i
return i._internal_value
CPython can solve this in C using an unsafe cast, and the code that checks for allowable subclasses of int actually ensures such a cast will work. But it still feels wrong; __int__ should be expected to do the work.
+1, that's why it's called "__int__" (even if it returns a PyLong in Py3 ;) Stefan
On Thu, Apr 4, 2013 at 8:01 AM, Chris Angelico
On Fri, Apr 5, 2013 at 1:59 AM, Guido van Rossum
wrote: On Thu, Apr 4, 2013 at 7:47 AM, Chris Angelico
wrote: Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
A class can define a __new__ method that returns a different object. E.g. (python 3):
Right, I'm aware it's possible. But who would expect it of a class?
If it's documented you could expect it. -- --Guido van Rossum (python.org/~guido)
On 5 Apr 2013 01:07, "Chris Angelico"
On Fri, Apr 5, 2013 at 1:59 AM, Guido van Rossum
wrote: On Thu, Apr 4, 2013 at 7:47 AM, Chris Angelico
wrote: Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object. Why should int be any different? What have I missed here?
A class can define a __new__ method that returns a different object.
E.g.
(python 3):
Right, I'm aware it's possible. But who would expect it of a class?
Python 3.3 does it for OSError to map errno values to the appropriate subclasses. That's mainly to aid migration to the new exception structure, though (see PEP 3151). For a clean slate API design you would use a separate factory function or class method to do the conversion. Cheers, Nick.
ChrisA _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe:
http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com
On 2013-04-04, at 16:47 , Chris Angelico wrote:
Sure, I could override __new__ to do stupid things
Or to do perfectly logical and sensible things, such as implementing "cluster classes" or using the base class as a factory of sorts.
in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object.
The problem is the expectation of what "a Foo object" is: type-wise, any Bar object is also a Foo object. I would not expect Foo() to return an object of a completely unrelated type, but returning an object of a subtype? That does not seem outlandish.
Le Fri, 5 Apr 2013 01:47:45 +1100,
Chris Angelico
class Foo: pass
class Bar(Foo): pass
Is there any argument that I can pass to Foo() to get back a Bar()? Would anyone expect there to be one? Sure, I could override __new__ to do stupid things, but in terms of logical expectations, I'd expect that Foo(x) will return a Foo object, not a Bar object.
OSError(errno.ENOENT, "couldn't find that file") FileNotFoundError(2, "couldn't find that file")
Regards Antoine.
On 05/04/13 01:23, Oscar Benjamin wrote:
The reason for calling int(obj) is to get an object that is precisely of type int. When I call this I do not want any modified or additional methods or data attached to the resulting object.
When I call int(), I'm expecting an int. That includes well-behaved subclasses of int that continue to behave like ints in all the ways that matter. It's curious to see the (d)evolution of thinking on type checking in Python circles. Once upon a time, type checking was discouraged, duck-typing was encouraged, and the philosophy was that an int is anything that behaves like an int. Now type-checking is tolerated or even encouraged, provided that you use isinstance, and an int is anything that inherits from the builtin (or the ABC). And this proposed change in behaviour continues the move away from Python's former emphasis on duck-typing. Now an int is only the builtin. Oscar, a question: what harm does it do to you if the int you receive has additional methods or data attached? I can appreciate Guido's concern that (say) a subclass might mess with the repr of the number, and he doesn't want that. (I don't find that argument compelling, but it does make sense.) But I don't understand why you would care if the int you receive happens to have an additional method or data that you don't expect. [...]
I think it's fair to say that there are times when someone wants an object that is precisely of type int. They should be able to rely on int(obj) returning an int or raising an error.
The first part of that is correct, but I disagree on the second. The 90-10 rule applies here. 90% of the time, we shouldn't care whether we receive an actual builtin int, only that we receive something that quacks like an int. In practice, that generally means something that inherits from int. IMO, int() and __int__ should support the common case, and not enforce the uncommon case
This is true similarly for __index__.
I have no argument with the idea that __index__ should guarantee to return a builtin int. I thought it already did make that guarantee. -- Steven
On 4/4/2013 10:04 PM, Steven D'Aprano wrote:
When I call int(), I'm expecting an int.
We agree so far...,
That includes well-behaved subclasses of int that continue to behave like ints in all the ways that matter.
but not here. I currently expect an actual int instance for 3 reasons. 1. As I read the doc, that is the currently documented 'should'. 2. I believe class constructors should, generally, return an instance of the class, in the narrow sense, and that factory functions should, generally, be used to return instances of multiple classes. The multiple classes would typically, or at least often, all be subclasses of some baseclass. 3. Most apropos to your next paragraph: *because* Python is duck-typed, I would not replace a function arg that might be an int subclass with int(arg) unless (I thought) the difference would make a difference. Lets consider cases: 1. int(non-scalar-number): this is usually an error, except for bytes or unicode strings that represents a number in standard base-[2-32] notation. int has builtin knowledge of these two builtin classes. One can add an __int__ method to string subclasses that represent integers with non-standard notation. A common use is int(input(prompt)) or int(other-external-input). 2. int(rational): for floats, Fractions, and Decimals, this returns the integral part, truncating toward 0. Decimal and float have __int__ methods. Fractions, to my surprise, does not, so int must use __floor__ or __round__ as a backup. I believe we have no disagreement that int() should return an int for these cases. Here is a possible use for input checking. def fib(n): "return fibonnaci(integral input); return type == input type" if int(n) != n or n < 0: raise TypeError('fib input must be a count') # let int() or < exception propagate # the input check is needed to prevent infinite looping <calculate with input n> return fib-of-n Because of duck-typing, there is no need to replace n with int(n). The return type will be the input type. 3. int(int-subclass-instance): If the int subclass instances behave like an int in all ways that matter in the context, there is no reason to specifically do. In other words, this use should be very rare, such as wanting the int repr. I am also not sure, without checking the doc for the definition of the bit operations, if all int subclasses would dependably substitute in bit manipulations. So unless you can give a good reason otherwise, I think the subclass .__int__ class should assume that the programmer might actually specifically want an int instance. In the example above, int() is part of a general case 2 input check that non-negative subclass instances should pass whether they return themselves or an int.
It's curious to see the (d)evolution of thinking on type checking in Python circles. Once upon a time, type checking was discouraged, duck-typing was encouraged, and the philosophy was that an int is anything that behaves like an int.
I always differentiated 'int' as a type/class int object from 'integer' as anything that behaves like an 'int'. For me, and the function outlined above, that includes integer-values rationals along with int subclass instances.
Now type-checking is tolerated or even encouraged, provided that you use isinstance,
I seem to have missed the encouragement.
and an int is anything that inherits from the builtin (or the ABC). And this proposed change in behaviour
To conform to how come read the doc..
continues the move away from Python's former emphasis on duck-typing.
For the reason explained above, I do not see this issue in such apocalyptic terms ;-) -- Terry Jan Reedy
On Fri, Apr 5, 2013 at 6:34 PM, Terry Jan Reedy
2. int(rational): for floats, Fractions, and Decimals, this returns the integral part, truncating toward 0. Decimal and float have __int__ methods. Fractions, to my surprise, does not, so int must use __floor__ or __round__ as a backup.
It uses __trunc__, which is supposed to be the unambiguous "Yes I really want to throw away the fractional part and risk losing information" replacement for __int__. int() will try __int__ first, and then __trunc__, as per PEP 3141. Mark
On Apr 03, 2013, at 09:17 PM, Nick Coghlan wrote:
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4?
I definitely agree with doing this for __int__(), since it's intimately tied to int(), which is clearly a type conversion operation. It's analogous to all the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer. __index__() is a bit trickier because it is not tied directly to type conversion. In this case, int subclasses could be valid, and as Hrvoje later points out, returning int-subclasses from __index__() should still work for all valid use cases. (Bug 17576 would still be a bug in this scenario, since obj.__index__() still needs to be called by operator.index() even when it's an int subclass.)
(I like the idea of an explicit error over implicit conversion to the base type, so deprecation of subtypes makes sense as a way forward. We should check the other type coercion methods, too.)
+1 -Barry
On 4 Apr 2013 00:18, "Barry Warsaw"
On Apr 03, 2013, at 09:17 PM, Nick Coghlan wrote:
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4?
I definitely agree with doing this for __int__(), since it's intimately
tied
to int(), which is clearly a type conversion operation. It's analogous to all the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
__index__() is a bit trickier because it is not tied directly to type conversion. In this case, int subclasses could be valid, and as Hrvoje later points out, returning int-subclasses from __index__() should still work for all valid use cases.
Implementing __index__ just means "This type can be converted to a Python integer without losing information". Aside from that extra "without information loss" qualification, it's the same as __int__. Cheers, Nick.
(Bug 17576 would still be a bug in this scenario, since obj.__index__()
needs to be called by operator.index() even when it's an int subclass.)
(I like the idea of an explicit error over implicit conversion to the
still base
type, so deprecation of subtypes makes sense as a way forward. We should check the other type coercion methods, too.)
+1
-Barry _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com
On 04/03/2013 08:14 AM, Nick Coghlan wrote:
On 4 Apr 2013 00:18, "Barry Warsaw"
mailto:barry@python.org> wrote: __index__() is a bit trickier because it is not tied directly to type conversion. In this case, int subclasses could be valid, and as Hrvoje later points out, returning int-subclasses from __index__() should still work for all valid use cases.
Implementing __index__ just means "This type can be converted to a Python integer without losing information". Aside from that extra "without information loss" qualification, it's the same as __int__.
How is that possible? Whether int or int subclass, if I'm implementing __index__ it means my type is not an int subclass, and when I return an int I most certainly have lost information from the original type. -- ~Ethan~
Le Wed, 03 Apr 2013 08:21:22 -0700,
Ethan Furman
On 04/03/2013 08:14 AM, Nick Coghlan wrote:
On 4 Apr 2013 00:18, "Barry Warsaw"
mailto:barry@python.org> wrote: __index__() is a bit trickier because it is not tied directly to type conversion. In this case, int subclasses could be valid, and as Hrvoje later points out, returning int-subclasses from __index__() should still work for all valid use cases.
Implementing __index__ just means "This type can be converted to a Python integer without losing information". Aside from that extra "without information loss" qualification, it's the same as __int__.
How is that possible? Whether int or int subclass, if I'm implementing __index__ it means my type is not an int subclass, and when I return an int I most certainly have lost information from the original type.
Without losing information about the numeric value. Regards Antoine.
On Apr 04, 2013, at 01:14 AM, Nick Coghlan wrote:
Implementing __index__ just means "This type can be converted to a Python integer without losing information". Aside from that extra "without information loss" qualification, it's the same as __int__.
Hmm. object.__index__(self) Called to implement operator.index(). Also called whenever Python needs an integer object (such as in slicing, or in the built-in bin(), hex() and oct() functions). Must return an integer. operator.index(a) operator.__index__(a) Return a converted to an integer. Equivalent to a.__index__(). bin(x) Convert an integer number to a binary string. The result is a valid Python expression. If x is not a Python int object, it has to define an __index__() method that returns an integer. Certainly, in slicing an int subclass will work fine:
class myint(int): ... pass ... range(10)[myint(2):myint(3)] range(2, 3)
Same goes for hex/oct/bin:
hex(myint(4)) '0x4' bin(myint(4)) '0b100' oct(myint(4)) '0o4'
Also, hex/oct/bin aren't types, they're functions, so this doesn't seem equivalent to int or str for me. The latter two are much more equivalent to the built-in tuple, dict, and list types. So I don't think there's any inconsistency in allowing int subclasses to be returned from __index__(), and nothing should break, so I see no reason to change the status quo here.
class anint(int): ... def __index__(self): ... return myint(self) ... from operator import index type(index(anint(7)))
Aside: It seems a bit odd to me that bin/hex/oct are defined to use __index__() instead of __int__(), but that's probably not worth arguing about. Cheers, -Barry
On 04/04/13 01:16, Barry Warsaw wrote:
On Apr 03, 2013, at 09:17 PM, Nick Coghlan wrote:
Perhaps we should start emitting a DeprecationWarning for int subclasses returned from __int__ and __index__ in 3.4?
I definitely agree with doing this for __int__(), since it's intimately tied to int(), which is clearly a type conversion operation. It's analogous to all the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
Why must it? I think that's the claim which must be justified, not just taken as a given. When we call n = int(something), what's the use-case for caring that n is an instance of built-in int but not of a subclass, and is that use-case so compelling that it must be enforced for all uses of int() etc.? As I see it, what I expect is this: n = int(something) assert isinstance(n, int) not this: n = int(something) assert type(n) is int I haven't cared about checking type identity since "Unifying types and classes" way back in Python 2.2, and I don't see why we should enforce a more restrictive rule now. -- Steven
I always intended for int() and str() to case subclasses to the built-in base class, and I don't want to change that rule. Consider a subclass of int() that overrides __repr__() and __str__() to print something fancy (maybe it defaults to hex; maybe it's an enum :-). I want to be able to say repr(int(x)) and get the standard decimal representation. Same with strings. If int() or str() were allowed to return a subclass instance, this wouldn't work, and I'd have to resort to draconian measures. (Betcha the first few solutions you come up with don't even work. :-) There are plenty of other use cases where a trivial subclass of one of the built-in types is used as some kind of "flag" -- e.g. maybe for values that ought to be serialized differently, or to enable some kind of type checking -- but it should always be possible to convert such values to the underlying base class to remove the special treatment. -- --Guido van Rossum (python.org/~guido
On 04/04/13 03:36, Guido van Rossum wrote:
Consider a subclass of int() that overrides __repr__() and __str__() to print something fancy (maybe it defaults to hex; maybe it's an enum :-). I want to be able to say repr(int(x)) and get the standard decimal representation. Same with strings. If int() or str() were allowed to return a subclass instance, this wouldn't work, and I'd have to resort to draconian measures.
int and str currently are allowed to return subclass instances.
(Betcha the first few solutions you come up with don't even work. :-)
Well, I'm always game to learn something. Challenge accepted. # Force a string subclass s to a built-in string. ''.join(s) # Force an int subclass n to a built-in int. (0).__add__(n) # And similarly for float subclass. 0.0.__add__(x) -- Steven
On Apr 04, 2013, at 03:04 AM, Steven D'Aprano wrote:
On 04/04/13 01:16, Barry Warsaw wrote:
the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
Why must it? I think that's the claim which must be justified, not just taken as a given. When we call n = int(something), what's the use-case for caring that n is an instance of built-in int but not of a subclass, and is that use-case so compelling that it must be enforced for all uses of int() etc.?
It's a consistency-of-implementation issue. Where built-in types are callable, they return concrete instances of themselves. This is true for e.g. list, tuple, dict, bytes, str, and should also be true of int. -Barry
On 04/03/2013 10:46 AM, Barry Warsaw wrote:
On Apr 04, 2013, at 03:04 AM, Steven D'Aprano wrote:
On 04/04/13 01:16, Barry Warsaw wrote:
the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
Why must it? I think that's the claim which must be justified, not just taken as a given. When we call n = int(something), what's the use-case for caring that n is an instance of built-in int but not of a subclass, and is that use-case so compelling that it must be enforced for all uses of int() etc.?
It's a consistency-of-implementation issue. Where built-in types are callable, they return concrete instances of themselves. This is true for e.g. list, tuple, dict, bytes, str, and should also be true of int.
+1
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 04/03/2013 01:50 PM, Ethan Furman wrote:
On 04/03/2013 10:46 AM, Barry Warsaw wrote:
On Apr 04, 2013, at 03:04 AM, Steven D'Aprano wrote:
On 04/04/13 01:16, Barry Warsaw wrote:
the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
Why must it? I think that's the claim which must be justified, not just taken as a given. When we call n = int(something), what's the use-case for caring that n is an instance of built-in int but not of a subclass, and is that use-case so compelling that it must be enforced for all uses of int() etc.?
It's a consistency-of-implementation issue. Where built-in types are callable, they return concrete instances of themselves. This is true for e.g. list, tuple, dict, bytes, str, and should also be true of int.
Given that requirement, we still don't have to mandate that __int__ return an actual instance of the int type: the coercion could happen inside int() (as it would for any non-subclass). Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with undefined - http://www.enigmail.net/ iEYEARECAAYFAlFcgVcACgkQ+gerLs4ltQ4ScwCfScssK/Cv74lPitQxbygmk5h/ RGoAnj2yUEgmEgorJi8GZh0GEB/iJrN1 =0I+y -----END PGP SIGNATURE-----
On 04/03/2013 12:21 PM, Tres Seaver wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 04/03/2013 01:50 PM, Ethan Furman wrote:
On 04/03/2013 10:46 AM, Barry Warsaw wrote:
On Apr 04, 2013, at 03:04 AM, Steven D'Aprano wrote:
On 04/04/13 01:16, Barry Warsaw wrote:
the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
Why must it? I think that's the claim which must be justified, not just taken as a given. When we call n = int(something), what's the use-case for caring that n is an instance of built-in int but not of a subclass, and is that use-case so compelling that it must be enforced for all uses of int() etc.?
It's a consistency-of-implementation issue. Where built-in types are callable, they return concrete instances of themselves. This is true for e.g. list, tuple, dict, bytes, str, and should also be true of int.
Given that requirement, we still don't have to mandate that __int__ return an actual instance of the int type: the coercion could happen inside int() (as it would for any non-subclass).
I don't understand. A non-int could only become an int via __int__, which int() calls. What magic is there in int() to turn any arbitrary object into an integer? --> class NonInt(): ... def __str__(self): ... return "Not an integer!" ... --> ni = NonInt() --> ni <__main__.NonInt object at 0x267a090> --> str(ni) 'Not an integer!' --> int(ni) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: int() argument must be a string or a number, not 'NonInt' -- ~Ethan~
On 4/3/2013 3:36 PM, Ethan Furman wrote:
On 04/03/2013 12:21 PM, Tres Seaver wrote:
Given that requirement, we still don't have to mandate that __int__ return an actual instance of the int type: the coercion could happen inside int() (as it would for any non-subclass).
I don't understand. A non-int could only become an int via __int__, which int() calls. What magic is there in int() to turn any arbitrary object into an integer?
From the 2.7 manual: " object.__complex__(self) object.__int__(self) object.__long__(self) object.__float__(self) Called to implement the built-in functions complex(), int(), long(), and float(). Should return a value of the appropriate type." I have always understood 'value of the appropriate type' to mean just what Guido says he intended it to mean. Changing to 'instance of the class or subclass thereof' is a change.
On Apr 03, 2013, at 01:46 PM, Barry Warsaw wrote:
It's a consistency-of-implementation issue. Where built-in types are callable, they return concrete instances of themselves. This is true for e.g. list, tuple, dict, bytes, str, and should also be true of int.
Well, I guess it's consistent for some of those built-in types <wink>. -Barry
On 2013-04-03, at 19:46 , Barry Warsaw wrote:
On Apr 04, 2013, at 03:04 AM, Steven D'Aprano wrote:
On 04/04/13 01:16, Barry Warsaw wrote:
the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
Why must it? I think that's the claim which must be justified, not just taken as a given. When we call n = int(something), what's the use-case for caring that n is an instance of built-in int but not of a subclass, and is that use-case so compelling that it must be enforced for all uses of int() etc.?
It's a consistency-of-implementation issue. Where built-in types are callable, they return concrete instances of themselves. This is true for e.g. list, tuple, dict, bytes, str, and should also be true of int.
FWIW unless I missed something it's true for none of bytes, str or
float, though it's true for complex (for some reason):
types = (int, float, complex, bytes, str)
Obj = type('Obj', (), {
'__{0.__name__}__'.format(t): (lambda t: lambda self:
type('my_{0.__name__}'.format(t), (t,), {})())(t)
for t in types
})
obj = Obj()
for t in types:
print("{} = {} ? {}".format(t, type(t(obj)), type(t(obj)) is t))
> python3 test.py
On 04/03/2013 09:04 AM, Steven D'Aprano wrote:
On 04/04/13 01:16, Barry Warsaw wrote:
It's analogous to all the other built-in types-as-functions, so int() calls __int__() which must return a concrete integer.
Why must it? I think that's the claim which must be justified, not just taken as a given.
Principle Of Least Surprise. My observation of the thread is that the majority of people feel "of course __int__ needs to return a real int". People are surprised that you can return subclasses. Nick thought there was an explicit check to prevent it! Why is this so surprising? I think it's because int, str, and float are all classes. Therefore, as callables they are constructors. When you call a constructor, you expect to get an instance of that specific class. Yes, it's always been possible with new-style classes to return a subclass from the constructor. But calling a constructor and getting a subclass is always surprising behavior. Also, permitting subclasses means the interface becomes conceptually far more complicated. We would need to restrict it to subclasses that don't hijack the representation. It's plausible, for example, to write a subclass of str that uses a novel approach for storing the string data, say as a rope. It could overload all the magic methods and behave identically to a str in every important way. But if its __str__ returned self instead of a real str object, that means that PyUnicode_AS_UNICODE would blissfully return the bypassed internal string value, whatever it is, and Python breaks. So we can't enforce it programmatically, we'd need to document it as convention. But explaining that is complicated, and If The Implementation Is Hard To Explain, It's A Bad Idea.
When we call n = int(something), what's the use-case for caring that n is an instance of built-in int but not of a subclass, and is that use-case so compelling that it must be enforced for all uses of int() etc.?
I'm much more interested in your counter use-case. When is it appealing or necessary to you to return a subclass of int from __int__()? In your postings so far you've said that this makes sense to you, but you haven't said why you need it. In lieu of a compelling use case, my vote is firmly against surprise and complexity. //arry/
and two instances i1 = Int1() i2 = Int2()
we get the following behaviour:
type(int(i1))
I would have expected 'Int1'
type(float(i1))
type(float(i2))
isinstance(int(i1), int) True
isinstance(int(i2), int) new
True
isinstance(float(i1), float) True isinstance(float(i2), float) True
why is printing new
participants (23)
-
Antoine Pitrou
-
Barry Warsaw
-
Cameron Simpson
-
Chris Angelico
-
Eric Snow
-
Ethan Furman
-
francis
-
Georg Brandl
-
Guido van Rossum
-
Hrvoje Niksic
-
Larry Hastings
-
Maciej Fijalkowski
-
Mark Dickinson
-
Mark Shannon
-
Nick Coghlan
-
Oscar Benjamin
-
Robert Kern
-
Stefan Behnel
-
Steven D'Aprano
-
Terry Jan Reedy
-
Tim Delaney
-
Tres Seaver
-
Xavier Morel