automation of __repr__/__str__ for all the common simple cases

Hi, in my experience for many cases, __repr__ and __str__ can be unconditionally be represented as simple string formatting operation, so i would propose to add a extension to support simply declaring them in the form of newstyle format strings a basic implementation for __repr__ could look like: class SelfFormatter(string.Formatter): def __init__(self, obj): self.__obj = obj string.Formatter.__init__(self) def get_value(self, key, args, kwargs): if isinstance(key, str) and hasattr(self.__obj, key): return getattr(self.__obj, key) return Formatter.get_value(self, key, args, kwargs) class SimpleReprMixing(object): _repr_ = '<{__class__.__name__} at 0x{__id__!x}>' def __repr__(self): formatter = SelfFormatter(self) return formatter.vformat(self._repr_, (), {'__id__':id(self)}) -- Ronny Pfannschmidt

I think that a generic __repr__ has been reinvented more times than I can count. I don't think a generic __str__ is a good thing, as it is supposed to be a pretty, semantically meaningful. I don't really see anywhere in the standard library that such a feature would make sense though. I feel like Python's standard library bloat actually makes the good stuff harder to find, and a better approach would be to have a minimal "core" standard library with a few "official" battery pack style libs that are very prominently featured and available. Since you might find this useful, here is my old __repr__ reciple (which has several issues, but gets the job done for the most part): def get_attributes(o): attributes = [(a, getattr(o, a)) for a in set(dir(o)).difference(dir(object)) if a[0] != "_"] return {a[0]: a[1] for a in attributes if not callable(a[1])} class ReprMixin(object): def _format(self, v): if isinstance(v, (basestring, date, time, datetime)): v = "'%s'" % v return v.encode("utf-8", errors="ignore") else: return v def __repr__(self): attribute_string = ", ".join("%s=%s" % (k[0], self._format(k[1])) for k in get_attributes(self).items()) return "%s(%s)" % (type(self).__name__, attribute_string) There is a similar recipes in SQL Alchemy, and I've seen them in a few other popular libs that I can't remember off the top of my head. Nathan

I think the documentation is pretty well organized overall. There is an issue of irreducible complexity though; someone that is searching for a specific thing wouldn't care, but a newer user trying to get their bearings on how this python thing works by browsing the standard lib probably would. Additionally, decoupling modules from the interpreter release schedule would probably be a good thing. Nathan

On Thu, Feb 16, 2012 at 1:34 AM, Nathan Rice <nathan.alexander.rice@gmail.com> wrote:
Python 3's reprlib already provides some tools for writing well-behaved __repr__ implementations (specifically, the reprlib.recursive_repr decorator that handles cycles in container representations). I actually have a recipe for simple "cls(arg1, arg2, arg3)" style __repr__ output on Stack Overflow: http://stackoverflow.com/questions/7072938/including-a-formatted-iterable-as...
There is a similar recipes in SQL Alchemy, and I've seen them in a few other popular libs that I can't remember off the top of my head.
The unfortunate part of dict-based __repr__ implementations is that the order of the parameter display is technically arbitrary. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Feb 16, 2012 at 4:24 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
Yes, but relying on OrderedDict rules out using keyword arguments to make any API easier to use. At that point, it's generally simpler for people to write their own repr that does exactly what they want. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Ronny Pfannschmidt wrote:
In my experience, not so much.
so i would propose to add a extension to support simply declaring them in the form of newstyle format strings
Declare them how? What is your proposed API for using this new functionality? Before proposing an implementation, you should propose an interface.
I don't think you need this just to get a generic "instance at id" string. If you inherit from object (and remember that all classes inherit from object in Python 3) you get this for free:
-- Steven

On 02/16/2012 12:43 AM, Steven D'Aprano wrote:
seems like you completely missed that class-level attributes can easily be redefined by subclasses like class User(SimpleReprMixin): _repr_ = "<User {name!r}>" ... the implementation and the interface is pretty simple and straightforward

On Thu, Feb 16, 2012 at 4:54 PM, Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> wrote:
the implementation and the interface is pretty simple and straightforward
However, the question is whether it's simple and straightforward enough to be worth standardising. There are a few common patterns that recur in repr implementations: - <type(obj) & id(obj)> based (the object.__repr__ default) - cls(arg1, arg2...) positional argument based - cls(kwd1=arg1, kwd2=arg2...) keyword argument based - a mixture of the previous two options Adding some helpers along those lines to reprlib may make sense, but a meaningful ReprMixin places stronger constraints on the relationship between class attributes and the desired repr output than is appropriate for the standard library. It's way too idiosyncratic across developers to be worth promoting in the stdlib (a project or domain specific class hierarchy is a different story, but that's not relevant for a general purpose ReprMixin proposal). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 02/16/2012 08:13 AM, Nick Coghlan wrote:
instead of the ReprMixin, how about a descriptor the Api would change to something like class User(object): __repr__ = FormatRepr('<User {name!r}') its less concise, but actually more straightforward and better decoupled (thanks for hinting at the strong coupling in the class hierarchy) and ArgRepr and KwargRepr could be added in a similar fashion Cheers, Ronny
Cheers, Nick.

+1 for not using inheritance (which honestly creates about as many problems as it solves)... Having some of the common use cases like this as descriptors that could be imported and used directly would be an improvement on the current interface of the lib IMHO. Nathan

I think that a generic __repr__ has been reinvented more times than I can count. I don't think a generic __str__ is a good thing, as it is supposed to be a pretty, semantically meaningful. I don't really see anywhere in the standard library that such a feature would make sense though. I feel like Python's standard library bloat actually makes the good stuff harder to find, and a better approach would be to have a minimal "core" standard library with a few "official" battery pack style libs that are very prominently featured and available. Since you might find this useful, here is my old __repr__ reciple (which has several issues, but gets the job done for the most part): def get_attributes(o): attributes = [(a, getattr(o, a)) for a in set(dir(o)).difference(dir(object)) if a[0] != "_"] return {a[0]: a[1] for a in attributes if not callable(a[1])} class ReprMixin(object): def _format(self, v): if isinstance(v, (basestring, date, time, datetime)): v = "'%s'" % v return v.encode("utf-8", errors="ignore") else: return v def __repr__(self): attribute_string = ", ".join("%s=%s" % (k[0], self._format(k[1])) for k in get_attributes(self).items()) return "%s(%s)" % (type(self).__name__, attribute_string) There is a similar recipes in SQL Alchemy, and I've seen them in a few other popular libs that I can't remember off the top of my head. Nathan

I think the documentation is pretty well organized overall. There is an issue of irreducible complexity though; someone that is searching for a specific thing wouldn't care, but a newer user trying to get their bearings on how this python thing works by browsing the standard lib probably would. Additionally, decoupling modules from the interpreter release schedule would probably be a good thing. Nathan

On Thu, Feb 16, 2012 at 1:34 AM, Nathan Rice <nathan.alexander.rice@gmail.com> wrote:
Python 3's reprlib already provides some tools for writing well-behaved __repr__ implementations (specifically, the reprlib.recursive_repr decorator that handles cycles in container representations). I actually have a recipe for simple "cls(arg1, arg2, arg3)" style __repr__ output on Stack Overflow: http://stackoverflow.com/questions/7072938/including-a-formatted-iterable-as...
There is a similar recipes in SQL Alchemy, and I've seen them in a few other popular libs that I can't remember off the top of my head.
The unfortunate part of dict-based __repr__ implementations is that the order of the parameter display is technically arbitrary. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Feb 16, 2012 at 4:24 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
Yes, but relying on OrderedDict rules out using keyword arguments to make any API easier to use. At that point, it's generally simpler for people to write their own repr that does exactly what they want. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Ronny Pfannschmidt wrote:
In my experience, not so much.
so i would propose to add a extension to support simply declaring them in the form of newstyle format strings
Declare them how? What is your proposed API for using this new functionality? Before proposing an implementation, you should propose an interface.
I don't think you need this just to get a generic "instance at id" string. If you inherit from object (and remember that all classes inherit from object in Python 3) you get this for free:
-- Steven

On 02/16/2012 12:43 AM, Steven D'Aprano wrote:
seems like you completely missed that class-level attributes can easily be redefined by subclasses like class User(SimpleReprMixin): _repr_ = "<User {name!r}>" ... the implementation and the interface is pretty simple and straightforward

On Thu, Feb 16, 2012 at 4:54 PM, Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> wrote:
the implementation and the interface is pretty simple and straightforward
However, the question is whether it's simple and straightforward enough to be worth standardising. There are a few common patterns that recur in repr implementations: - <type(obj) & id(obj)> based (the object.__repr__ default) - cls(arg1, arg2...) positional argument based - cls(kwd1=arg1, kwd2=arg2...) keyword argument based - a mixture of the previous two options Adding some helpers along those lines to reprlib may make sense, but a meaningful ReprMixin places stronger constraints on the relationship between class attributes and the desired repr output than is appropriate for the standard library. It's way too idiosyncratic across developers to be worth promoting in the stdlib (a project or domain specific class hierarchy is a different story, but that's not relevant for a general purpose ReprMixin proposal). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 02/16/2012 08:13 AM, Nick Coghlan wrote:
instead of the ReprMixin, how about a descriptor the Api would change to something like class User(object): __repr__ = FormatRepr('<User {name!r}') its less concise, but actually more straightforward and better decoupled (thanks for hinting at the strong coupling in the class hierarchy) and ArgRepr and KwargRepr could be added in a similar fashion Cheers, Ronny
Cheers, Nick.

+1 for not using inheritance (which honestly creates about as many problems as it solves)... Having some of the common use cases like this as descriptors that could be imported and used directly would be an improvement on the current interface of the lib IMHO. Nathan
participants (7)
-
Edward Lesmes
-
Nathan Rice
-
Ned Batchelder
-
Nick Coghlan
-
Ronny Pfannschmidt
-
Serhiy Storchaka
-
Steven D'Aprano