On Jan 21, 2020, at 12:29, Chris Angelico <ros...@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), 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 -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VMRQSQB3N6CR6NTKQR2GBUBZLHTWYDHH/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to