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/