On Jan 21, 2020, at 12:29, Chris Angelico <[email protected]> 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), while for the other 20%, I think it might make it too
easy to get things wrong.
Usually, the repr is either something you could type into the REPL to get an
equal object, or something that’s a SyntaxError (usually because it’s in angle
brackets). There are exceptions to that (like overlong or self-recursive
containers), but as a general rule it’s true. And there’s no check here that
the args in __repr__ are the same ones as in __new__/__init__, so it might be
way too easy (especially when modifying code) to produce something that looks
like a valid repr but isn’t—and may not even produce an error (e.g., you added
a new constructor param with a default value, and didn’t add it to kwargs, so
now every repr gives you a repr for something with the default value rather
than the actual value).
Maybe if there were a way to specify init and repr or new and repr together…
but at that point you might as well use dataclasses, right?
> 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.
However, it might be useful to have a dump-all-attributes function that gave
you an angle-bracket repr a la file objects, something like:
attrstr = ' '.join([f"{obj.__class__.__qualname__} object at {id(obj):x}“] +
[f"{attr} = {getattr(obj, attr)!r}" for attr in type(obj).__slots__]>
return f"<{attrstr}>"
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. 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:
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/VMRQSQB3N6CR6NTKQR2GBUBZLHTWYDHH/
Code of Conduct: http://python.org/psf/codeofconduct/