On Wed, Jan 22, 2020 at 9:17 AM Andrew Barnert <abarnert@yahoo.com> wrote:
On Jan 21, 2020, at 12:29, Chris Angelico <rosuav@gmail.com> wrote:
For non-dataclass classes, it would be extremely helpful to have an easy helper function available:
class Spam: def __repr__(self): return reprlib.kwargs(self, ["quality", "recipe", "ham"])
The implementation for this function would be very similar to what dataclasses already do:
# in reprlib.py def kwargs(obj, attrs): attrs = [f"{a}={getattr(obj, a)!r}" for a in attrs] return f"{obj.__class__.__qualname__}({", ".join(attrs)})"
A similar function for positional args would be equally easy.
I like this, but I think it’s still more complex than it needs to be for 80% of the cases (see below)
IMO that's not a problem. The implementation of reprlib.kwargs() is allowed to be complex, since it's buried away as, well, implementation details. As long as it's easy to call, that's all that matters.
while for the other 20%, I think it might make it too easy to get things wrong.
Hmm. I kept it completely explicit - it will generate a repr that shows the exact attributes listed (and personally, I'd often write it as "quality recipe ham".split()), and left the idea of automatic detection as a bikesheddable option.
Bikeshedding opportunity: Should it be legal to omit the attrs parameter, and have it use __slots__ or fall back to dir(obj) ?
This makes things even more dangerous. It’s very common for classes to have attributes that aren’t part of the constructor call, or constructor params that aren’t attributes, and this would give you the wrong answer.
It would be nice if there were a safe way to get the constructor-call-style repr. And I think there might be for 80% of the types—and the rest can specify it manually and take the risk of getting it wrong, probably.
One option is the pickle/copy protocol. If the type uses one of the newargs methods, you can use that to get the constructor arguments; if it uses one of the other pickling methods (or can’t be pickled), this just doesn’t work.
You could also look at the inspect.signature of __init__ and/or __new__. If every param has an attribute with the same name, use that; otherwise, this doesn’t work.
And if none of the automatic ways worked and you tried to use them anyway, you get an error.
This is definitely getting into the realm of magic. The question is, is it worth the convenience? I'd be +0.25 for the magic. Also, that can always be added in the future. If the explicit-list-of-attributes form is useful but too fiddly, it would be possible to add a stated default of "figure it out by magic", and then the exact magic can be redefined at will.
But it would be nice if this error were at class-defining time rather than at repr-calling time, so maybe a decorator is actually a better solution?
@reprlib.defaultrepr class Spam:
Also a definite possibility. ChrisA