On Mon, May 04, 2020 at 07:01:03PM +0100, Lewis Ball wrote:
> Hi All,
>
> First of all, if this is something which has been discussed in the past the
> please point me in the right direction.
It certainly has been, but with no conclusion one way or another. I
think people agree that it is a pain point, but there are no good ideas
for what to do about it.
You can probably find examples of past discussion in this mailing list's
archives, and on Python-List mailing list, or comp.lang.python if you
prefer Usenet. (Sorry, I don't have time at the moment to trawl the
archives.)
> *Problem:*
>
> When creating classes in Python, I find myself writing the __init__ method
> in a very similar way a lot of the time, that is:
> ```
> def __init__(self, argument_1, argument_2, argument_3=None):
> self.argument_1 = argument_1
> self.argument_2 = argument_2
> self.argument_3 = argument_3
> # then maybe some other attribute setting and logic follows
> ```
>
> Every argument of __init__ gets a corresponding attribute with the same
> name. This means that each `argument_i` has been typed 3 times, which seems
> overly-verbose as well as being easy to mistype.
Yes, and a similar anti-pattern also occurs when you have a method that
calls super, or some other method, with a series of `parameter=parameter`
calls. See the recent threads
* Keyword arguments self-assignment
* Keyword Unpacking Shortcut
last month.
[...]
> *Suggestion:*
>
> A new built-in called something like `assign()` which would assign every
> single __init__ arg to a corresponding attribute. e.g. the snippet from
> above could be rewritten to:
> ```
> def __init__(self, argument_1, argument_2, argument_3=None):
> assign()
> # other init logic goes here
> ```
One moderately common work around for this is to use **kwargs like so:
vars(self).update(**kwargs)
but that doesn't work when you have named parameters. (Or if you use
`__slots__`.) But we can make it work.
Proposal:
We should have a mechanism that collects the current function or
method's parameters into a dict, similar to the way locals() returns all
local variables.
This mechanism could be a new function,or it could even be a magic local
variable inside each function, similar to what is done to make super()
work. But for the sake of this discussion, I'll assume it is a function,
`parameters()`, without worrying about whether it is a built-in or
imported from the `inspect` module.
So given a function signature:
def func(spam, eggs, cheese=None):
and a call to it `func(1, 2)`, then inside the body of the function,
calling `parameters()` will return the dict:
{'spam': 1, 'eggs': 2, 'cheese': None}
It's just a dict, its not magic in any way at all, and the caller can
then process it however they like:
params = parameters()
del params['spam']
params['eggs'] + 1
vars(self).update(params)
but I expect that (with two exceptions) most of the use-cases will
involve no post-processing.
The two common exceptions may be:
- The first parameter to methods, usually (but not always) spelled
"self" in instance methods, or "cls" in class methods.
- Catch-all `*args` and `**kwargs`.
If dicts supported the difference operator, that would be easy to deal
with:
vars(self).update( parameters() - {'self', 'args', 'kw'} )
but they don't, so I don't know how best to handle this situation.
But however we deal with this, having the function simply return the
parameter list and their current values (at the moment `parameters()` is
called) gives the caller maximum flexibility.
for name, value in parameters().items():
setattr(self, name, value)
will work if you have `__slots__`, and it should be pretty obvious how
to skip unwanted parameters:
for name, value in parameters().items():
if name not in ('self', 'args', 'kwargs', 'spam'):
setattr(self, name, value)
Another pattern would be to pass on your parameters to the superclasses:
super().method(**parameters())
which would reduce the need for special keyword handling.
[...]
> Is this something that others would find useful?
Absolutely!
--
Steven
_______________________________________________
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/KEE3TE5ZFIQT3TG3XMTIPPROR6IGFPDB/
Code of Conduct: http://python.org/psf/codeofconduct/