Bring namedtuple's __str__ and __repr__ behavior to regular classes
Hi,
from collections import namedtuple A = namedtuple("A", ["foo"]) print(A(foo=1)) A(foo=1) str(A(foo=1)) 'A(foo=1)' repr(A(foo=1)) 'A(foo=1)'
The relevant code is https://hg.python.org/cpython/file/2.7/Lib/collections.py#l356 I propose we bring the behavior to regular classes. Instead of
class A(object): ... def __init__(self): ... self.foo = 1 ... repr(A()) '<__main__.A object at 0x1090c0990>'
We should be able to see the current values to the display.
repr(A()) 'A(foo=1)'
Reasons: 1. Helps debugging (via pdb, print and logging). We no longer have to do A().foo to find out. 2. I don't know how often people actually rely on repr(A()) or str(A()) and parse the string so breaking compatibility is, probably low. 3. People who wish to define their own repr and str is welcome. Django model for example has a more explicit representation by default (although many Django users do redefine the representation on their own). datetime.datetime by default, as a library, is also explicit. So customization will come. The main challenge: Where and how do we actually look for what attributes are relevant? namedtuple can do it because it has __slot__ and we know in advance how many attributes are set. In regular class, we deal with dynamic attribute setting, single and inheritances. I don't have an answer for this simply because I lack of experience. We can certainly start with the attributes set in the main instance and one level up in the inheritance chain. Other issues: 1. What if there are too many attributes? I don't think the number will explode beyond 30. I choose this number out of thin air. I can do more research on this. It doesn't actually hurt to see everything. If you do have a class with so many attribute (whether you have this many to begin with, or because you allow aritbary numbers of attributes to be set -- for example, a document from a collection in NoSQL like MongoDB), that's still very useful. We could limit by default up to how many. 2. How do we order them? We can order them in unsorted or sorted order. I prefer the sorted order.
On Mon, Sep 15, 2014 at 4:48 AM, John Wong
class A(object): ... def __init__(self): ... self.foo = 1 ... repr(A()) '<__main__.A object at 0x1090c0990>'
We should be able to see the current values to the display.
repr(A()) 'A(foo=1)'
Start with this: class object(object): def __repr__(self): return whatever_you_want_to_do Then whenever you subclass object in this module, you'll subclass your own subclass of object, and get your own repr. That's something that will work on all versions of Python, including 2.x which isn't going to get any changes like this. It's perfectly safe - it can't break anyone's code but your own - and if you stick that at the top of the file (or in another file and "from utils import object"), you don't have to change anything else, assuming you're explicitly subclassing object everywhere. ChrisA
By the way:
On Mon, Sep 15, 2014 at 4:48 AM, John Wong
The main challenge:
Where and how do we actually look for what attributes are relevant? namedtuple can do it because it has __slot__ and we know in advance how many attributes are set. In regular class, we deal with dynamic attribute setting, single and inheritances. I don't have an answer for this simply because I lack of experience. We can certainly start with the attributes set in the main instance and one level up in the inheritance chain.
This is a fundamentally hard problem. Obviously it's easy to see what attributes are set, but figuring out which are relevant is usually a job for the class itself. So what you might want to do is have a class attribute, and then have your custom __repr__ scan through all of __bases__, collecting up these "important attributes". Something like this: class Point2D(object): show_in_repr = "x", "y" ... class Point3D(Point2D): show_in_repr = "z" ... Then the repr for object could follow the chain, pick up all the show_in_repr class attributes, and even use that for the order (parents first, in inheritance order, then this class's attributes). But at this point, you're definitely in the realm of custom code, not changes to the language. Which is good, because you'll likely change your mind about the details, and it's easy to recode your top-level inherit :) ChrisA
On 15 Sep 2014 05:09, "Chris Angelico"
By the way:
On Mon, Sep 15, 2014 at 4:48 AM, John Wong
wrote: The main challenge:
Where and how do we actually look for what attributes are relevant? namedtuple can do it because it has __slot__ and we know in advance how
many
attributes are set. In regular class, we deal with dynamic attribute setting, single and inheritances. I don't have an answer for this simply because I lack of experience. We can certainly start with the attributes set in the main instance and one level up in the inheritance chain.
This is a fundamentally hard problem. Obviously it's easy to see what attributes are set, but figuring out which are relevant is usually a job for the class itself. So what you might want to do is have a class attribute, and then have your custom __repr__ scan through all of __bases__, collecting up these "important attributes".
More generally, this is the kind of situation where we're more likely to provide better tools for *writing* these kinds of representations, rather than providing them by default (the latter approach is too likely to go wrong). There are currently two such key plumbing modules: pprint: https://docs.python.org/3/library/pprint.html reprlib: https://docs.python.org/3/library/reprlib.html Redesigning the way pprint works to make it easier to customise is what Łukasz had in mind when writing PEP 443 to add functools.singledispatch to Python 3.4. It also makes it easier to write your own pretty printers and custom repr functions that fall back to object introspection to provide more details. reprlib is currently focused on providing object representations that aren't overwhelmingly long, even for large (or recursively nested) containers. However, I seem to recall also seeing proposals on the tracker to adding functions there that make it easier to emit named tuple style representations for objects. More generally, the idea of a "functionally equivalent named tuple" is also relevant to implementing hashing, equality comparisons and ordering operations in a sensible way, so there's actually potential here for a third party module dedicated specifically to making it easier to write classes that behave that way. Regards, Nick.
On 9/14/2014 3:08 PM, Chris Angelico wrote:
By the way:
On Mon, Sep 15, 2014 at 4:48 AM, John Wong
wrote: The main challenge:
Where and how do we actually look for what attributes are relevant? namedtuple can do it because it has __slot__ and we know in advance how many attributes are set. In regular class, we deal with dynamic attribute setting, single and inheritances. I don't have an answer for this simply because I lack of experience. We can certainly start with the attributes set in the main instance and one level up in the inheritance chain.
This is a fundamentally hard problem. Obviously it's easy to see what attributes are set, but figuring out which are relevant is usually a job for the class itself.
There is also the problem that the representation of even one value can
be arbitrarily long. Named tuples, as used in the stdlib, usually have
a relatively small number of fields with values with (currently) short
representations. That is not so in general.
A third problem is that the change would apply recursively. If a named
tuple now has an
participants (4)
-
Chris Angelico
-
John Wong
-
Nick Coghlan
-
Terry Reedy