Reprs of classes and functions
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up. Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
On Wed, Feb 25, 2015 at 10:12:03AM +0200, Serhiy Storchaka wrote:
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up.
Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
Do you mean like this? repr(int) => 'int' repr(int.from_bytes) => 'int.from_bytes' repr(open) => 'open' repr(collections.Counter) => 'collections.Counter' repr(collections.Counter.fromkeys) => 'collections.Counter.fromkeys' repr(collections.namedtuple) => 'collections.namedtuple' -1 on that idea. The suggested repr gives no clue as to what kind of object they are. Are they functions, methods, classes, some kind of Enum-like constant or something special like None? That hurts the usefulness of object reprs at the interactive interpreter. And it leads to weirdness like this: def spam(x): if not isinstance(x, int): raise TypeError('expected int, got %r' % x) # current behaviour spam(int) => TypeError: expected int, got <class 'int'> # proposed behaviour spam(int) => TypeError: expected int, got int As for them being evaluable, I don't think that's terribly useful. Do you have a use-case for that? I'm not saying that it will never be useful, but I don't think it is useful enough to make up for the loss of human-readable information (the type of the object). Being able to eval(repr(instance)) is sometimes handy, but I don't think eval(repr(type_or_function)) is useful. -- Steve
In [2]: import collections In [3]: collections.Counter.fromkeys Out[3]: <bound method type.fromkeys of <class 'collections.Counter'>> In [4]: f = collections.Counter.fromkeys In [5]: f Out[5]: <bound method type.fromkeys of <class 'collections.Counter'>>
On 25.02.15 12:25, Steven D'Aprano wrote:
On Wed, Feb 25, 2015 at 10:12:03AM +0200, Serhiy Storchaka wrote:
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
Do you mean like this?
repr(int) => 'int'
repr(int.from_bytes) => 'int.from_bytes'
Yes, it is.
-1 on that idea.
The suggested repr gives no clue as to what kind of object they are. Are they functions, methods, classes, some kind of Enum-like constant or something special like None? That hurts the usefulness of object reprs at the interactive interpreter. And it leads to weirdness like this:
def spam(x): if not isinstance(x, int): raise TypeError('expected int, got %r' % x)
This is uncommon case and bugprone code. Don't write so. spam('x'*1000000) or spam(()) will produce unhelpful error messages. Usually it is written as: def spam(x): if not isinstance(x, int): raise TypeError('expected int, got %s' % type(x).__name__)
A variant I've often wanted is to make str() return the "clean" type only (e.g. 'int') while leaving repr() alone -- this would address most problems Steven brought up. On Wed, Feb 25, 2015 at 12:12 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up.
Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
On Feb 25, 2015, at 8:21, Guido van Rossum <guido@python.org> wrote:
A variant I've often wanted is to make str() return the "clean" type only (e.g. 'int') while leaving repr() alone -- this would address most problems Steven brought up.
On further thought, I'm not sure backward compatibility can be dismissed. Surely code shouldn't care about the str of a callable, right? But yeah, sometimes it might care, if it's, say, a reflective framework for bridging or debugging or similar. I once wrote a library that exposed Python objects over AppleEvents. I needed to know all the bound methods of the object. For pure-Python objects, this is easy, but for builtin/extension objects, the bound methods constructed from both slot-wrapper and method-descriptor are of type method-wrapper. (The bug turned up when an obscure corner of our code that tried lxml, cElementTree, then ElementTree was run on a 2.x system without lxml installed, something we'd left out of the test matrix for that module.) When you print out the method, it's obvious what type it is. But how do you deal with it programmatically? Despite what the docs say, method-descriptor does not return true for either isbuiltin or isroutine. In fact, nothing in inspect is much help. And the type isn't in types. Printing out the method, it's dead obvious what it is. As it turned out, callable(x) and (hasattr(x, 'im_self') or hasattr(x, '__self__')) was good enough for my particular use case, at least in all Python interpreters/versions we supported (it even works in PyPy with CPyExt extension types and an early version of NumPyPy). But what would a good general solution be? I think dynamically constructing the type from int().__add__ and then verifying that all extension types in all interpreters and versions I care about pass isinstance(that) unless they look like pure-Python bound methods might be the best way. And even if I did resort to using the string representation, I'd probably use the repr rather than the str, and I'd probably look at type(x) rather than x itself. But still, I can imagine someone deciding "whatever I do is going to be hacky, because there's no non-hacky way to get this information, so I might as well go with the simplest hack that's worked on every CPython and PyPy version from 2.2 to today" and use "... or str(x).startswith('<method-wrapper')".
On Wed, Feb 25, 2015 at 12:12 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up.
Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
My first response seems to have bounced, so: Besides backward compatibility, there's still a minor downside to that: the current behavior helps catch some novice errors, like storing a function instead of calling the function and storing its return value. Printing `<function spam.cook`> instead of the expected `spam, spam, eggs, beans, spam` is a lot more obvious than seeing `cook` alone. But as I said, it's pretty minor. It's still not _that_ hard to understand from `cook`, especially if you ask someone for help, and this is the kind of mistake that novices only make once (and usually ask for help with). The benefit to everyone surely outweighs the small cost to novices. Sent from a random iPhone On Feb 25, 2015, at 8:21, Guido van Rossum <guido@python.org> wrote:
A variant I've often wanted is to make str() return the "clean" type only (e.g. 'int') while leaving repr() alone -- this would address most problems Steven brought up.
On Wed, Feb 25, 2015 at 12:12 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up.
Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
I didn't read all that, but my use case is that for typing.py (PEP 484) I want "nice" reprs for higher-order classes, and if str(int) returned just 'int' it would be much simpler to e.g. make the str() or repr() of Optional[int] return "Optional[int]". I've sort of solved this already in https://github.com/ambv/typehinting/blob/master/prototyping/typing.py, but it would still be nice if I didn't have to work so hard at it. On Wed, Feb 25, 2015 at 5:41 PM, Andrew Barnert < abarnert@yahoo.com.dmarc.invalid> wrote:
On Feb 25, 2015, at 8:21, Guido van Rossum <guido@python.org> wrote:
A variant I've often wanted is to make str() return the "clean" type only (e.g. 'int') while leaving repr() alone -- this would address most problems Steven brought up.
On further thought, I'm not sure backward compatibility can be dismissed. Surely code shouldn't care about the str of a callable, right? But yeah, sometimes it might care, if it's, say, a reflective framework for bridging or debugging or similar.
I once wrote a library that exposed Python objects over AppleEvents. I needed to know all the bound methods of the object. For pure-Python objects, this is easy, but for builtin/extension objects, the bound methods constructed from both slot-wrapper and method-descriptor are of type method-wrapper. (The bug turned up when an obscure corner of our code that tried lxml, cElementTree, then ElementTree was run on a 2.x system without lxml installed, something we'd left out of the test matrix for that module.)
When you print out the method, it's obvious what type it is. But how do you deal with it programmatically? Despite what the docs say, method-descriptor does not return true for either isbuiltin or isroutine. In fact, nothing in inspect is much help. And the type isn't in types. Printing out the method, it's dead obvious what it is.
As it turned out, callable(x) and (hasattr(x, 'im_self') or hasattr(x, '__self__')) was good enough for my particular use case, at least in all Python interpreters/versions we supported (it even works in PyPy with CPyExt extension types and an early version of NumPyPy). But what would a good general solution be?
I think dynamically constructing the type from int().__add__ and then verifying that all extension types in all interpreters and versions I care about pass isinstance(that) unless they look like pure-Python bound methods might be the best way. And even if I did resort to using the string representation, I'd probably use the repr rather than the str, and I'd probably look at type(x) rather than x itself. But still, I can imagine someone deciding "whatever I do is going to be hacky, because there's no non-hacky way to get this information, so I might as well go with the simplest hack that's worked on every CPython and PyPy version from 2.2 to today" and use "... or str(x).startswith('<method-wrapper')".
On Wed, Feb 25, 2015 at 12:12 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up.
Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
On 02/25/2015 09:12 AM, Serhiy Storchaka wrote:
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up.
Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly.
Like other, I would be -1 because of the increased potential for confusion (many people have not grasped the significance of repr() and its quoting of strings), and the loss of information (class/function/...). It would be nice to have the repr()s consistent, i.e. name always or never in quotes, and always or never add the memory address. But that's probably not worth breaking compatibility. Changing str(class) is different, since there's enough potential confusion in this representation anyway.
And such reprs are evaluable.
Only sometimes, unless you change the repr to "__import__(" + __module__ + ", None, None, ['*'])." + __qualname__ :) Georg
I plan on committing http://bugs.python.org/issue13224 tomorrowish (the patch is a little stale and I want to poke around some to be sure its ok). If anyone objects please follow up in the issue. -Rob On 26 February 2015 at 16:28, Guido van Rossum <guido@python.org> wrote:
I didn't read all that, but my use case is that for typing.py (PEP 484) I want "nice" reprs for higher-order classes, and if str(int) returned just 'int' it would be much simpler to e.g. make the str() or repr() of Optional[int] return "Optional[int]". I've sort of solved this already in https://github.com/ambv/typehinting/blob/master/prototyping/typing.py, but it would still be nice if I didn't have to work so hard at it.
On Wed, Feb 25, 2015 at 5:41 PM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Feb 25, 2015, at 8:21, Guido van Rossum <guido@python.org> wrote:
A variant I've often wanted is to make str() return the "clean" type only (e.g. 'int') while leaving repr() alone -- this would address most problems Steven brought up.
On further thought, I'm not sure backward compatibility can be dismissed. Surely code shouldn't care about the str of a callable, right? But yeah, sometimes it might care, if it's, say, a reflective framework for bridging or debugging or similar.
I once wrote a library that exposed Python objects over AppleEvents. I needed to know all the bound methods of the object. For pure-Python objects, this is easy, but for builtin/extension objects, the bound methods constructed from both slot-wrapper and method-descriptor are of type method-wrapper. (The bug turned up when an obscure corner of our code that tried lxml, cElementTree, then ElementTree was run on a 2.x system without lxml installed, something we'd left out of the test matrix for that module.)
When you print out the method, it's obvious what type it is. But how do you deal with it programmatically? Despite what the docs say, method-descriptor does not return true for either isbuiltin or isroutine. In fact, nothing in inspect is much help. And the type isn't in types. Printing out the method, it's dead obvious what it is.
As it turned out, callable(x) and (hasattr(x, 'im_self') or hasattr(x, '__self__')) was good enough for my particular use case, at least in all Python interpreters/versions we supported (it even works in PyPy with CPyExt extension types and an early version of NumPyPy). But what would a good general solution be?
I think dynamically constructing the type from int().__add__ and then verifying that all extension types in all interpreters and versions I care about pass isinstance(that) unless they look like pure-Python bound methods might be the best way. And even if I did resort to using the string representation, I'd probably use the repr rather than the str, and I'd probably look at type(x) rather than x itself. But still, I can imagine someone deciding "whatever I do is going to be hacky, because there's no non-hacky way to get this information, so I might as well go with the simplest hack that's worked on every CPython and PyPy version from 2.2 to today" and use "... or str(x).startswith('<method-wrapper')".
On Wed, Feb 25, 2015 at 12:12 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
This idea is already casually mentioned, but sank deep into the threads of the discussion. Raise it up.
Currently reprs of classes and functions look as:
int <class 'int'> int.from_bytes <built-in method from_bytes of type object at 0x826cf60> open <built-in function open> import collections collections.Counter <class 'collections.Counter'> collections.Counter.fromkeys <bound method Counter.fromkeys of <class 'collections.Counter'>> collections.namedtuple <function namedtuple at 0xb6fc4adc>
What if change default reprs of classes and functions to just full qualified name __module__ + '.' + __qualname__ (or just __qualname__ if __module__ is builtins)? This will look more neatly. And such reprs are evaluable.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Robert Collins <rbtcollins@hp.com> Distinguished Technologist HP Converged Cloud
participants (7)
-
Alexander Heger
-
Andrew Barnert
-
Georg Brandl
-
Guido van Rossum
-
Robert Collins
-
Serhiy Storchaka
-
Steven D'Aprano