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 -- 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/KEE3TE5ZFIQT3TG3XMTIPPROR6IGFPDB/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to