So, this thread has stalled, but I saw no contrary opinions to a
decorator like this.

I think the plain version (not treating positional only arguments differently,
no partially-initialized namespace, no annotations based auto-argument cast)
could make it into a bpo -

what do you say?

On Wed, 6 May 2020 at 11:52, Joao S. O. Bueno <jsbu...@python.org.br> wrote:
>
> Here -  with the current inspect.Signature, it is straightforward
> to get a decorator that can do that covering most, if not all,
> corner cases, and even adding some extra functionality:
>
> https://gist.github.com/jsbueno/f689a181d50384f627b43b9b2aabe4f2
>
> ```
>
> from inspect import signature, Parameter
> from functools import wraps, partial
>
> def autoassign(func=None, *, expand_kwargs=False):
>
>     if not func:
>         return partial(autoassign, expand_kwargs=expand_kwargs)
>     sig = signature(func)
>     @wraps(func)
>     def wrapper(*args, **kwargs):
>         instance = args[0]
>         bound_args = sig.bind(*args, **kwargs)
>         bound_args.apply_defaults()
>         for i, (key, value) in enumerate(bound_args.arguments.items()):
>             if i == 0 or sig.parameters[key].kind == 
> Parameter.POSITIONAL_ONLY:
>                 continue
>             if expand_kwargs and sig.parameters[key].kind ==
> Parameter.VAR_KEYWORD:
>                 for kwkey, kwvalue in value.items():
>                     setattr(instance, kwkey, kwvalue)
>                 continue
>             setattr(instance, key, value)
>         return func(*args, **kwargs)
>     return wrapper
>
> """
> class A:
>     @autoassign
>     def __init__(self, a, b, c=3):
>         pass
>
> a = A(1, 2)
> assert a.a == 1 and a.b == 2 and a.c == 3
> """
>
>
>
> ```
>
> On Wed, 6 May 2020 at 11:11, Joao S. O. Bueno <jsbu...@python.org.br> wrote:
> >
> > On Mon, 4 May 2020 at 19:13, Lewis Ball <lrjb...@gmail.com> wrote:
> > >
> > > I had a similar solution with a decorator using inspect.getfullargspec 
> > > but it is pretty fiddly and it is pretty easy to miss
> > >  some of the edge cases. Having a standard implementation would 
> > > definitely take care of this. And I imagine
> > > static analysis tools would be able to cope with it, they do a good job 
> > > with all of the current language features!
> >
> > I like this idea of having a decorator for that.
> > Since the decorator can apply the arguments to the instance without
> > actually fiddling into `locals` or on `__init__`
> > code.
> >
> > It could also be used _with_ dataclasses, suplying the assignment boiler
> > plate for when one wants to have an explicit __init__ method.
> > (yes, there is post_init, but as seem recently in a thread here, it is
> > tough to get it
> > working cooperatively)
> >
> > But I suppose
> >
> > @autoassign
> > def __init__(self, a, b, flag1, flag2, etcetera):
> >       ...
> >
> > could live in "functools" without causing great harm.
> >
> >
> >
> > >
> > > On Mon, 4 May 2020, 22:11 Steele Farnsworth, <swfarnswo...@gmail.com> 
> > > wrote:
> > >>
> > >> I agree that dataclasses are for a slightly different use case.
> > >>
> > >> It looks like this could be implemented as a decorator using the 
> > >> functionality afforded by `inspect.signature`, though what I've come up 
> > >> with so far is a bit clunky because you have to account for parameters 
> > >> that could be positional or keyword and assigning default values for 
> > >> missing arguments.
> > >>
> > >> If this were added, I assume that static analysis tools would need to be 
> > >> updated to account for the assumption that each instance has attributes 
> > >> with the same names that appear in the `__init__` signature, and I have 
> > >> no idea what that would entail. It would probably pose a similar issue 
> > >> for automated refactoring.
> > >>
> > >> On Mon, May 4, 2020 at 4:48 PM Lewis Ball <lrjb...@gmail.com> wrote:
> > >>>
> > >>> I did think about data classes and although I haven't really used them 
> > >>> much they do seem to be for a different use case, for example they 
> > >>> don't support keyword-only args or positional-only args. I'm not sure 
> > >>> if there are any other differences. Maybe a data class which supported 
> > >>> kW-only args and pos-only args would suit my use case.
> > >>>
> > >>> On Mon, 4 May 2020, 21:19 Henk-Jaap Wagenaar, 
> > >>> <wagenaarhenkj...@gmail.com> wrote:
> > >>>>
> > >>>> You are not the first to have this idea. Unless I am mistaken you 
> > >>>> might find what you are looking for in dataclasses which were added in 
> > >>>> Python 3.7:
> > >>>>
> > >>>> https://docs.python.org/3/library/dataclasses.html
> > >>>>
> > >>>> On Mon, 4 May 2020 at 19:06, Lewis Ball <lrjb...@gmail.com> 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.
> > >>>>>
> > >>>>> 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. This 
> > >>>>> pattern is easy to find in various popular python libraries, and in 
> > >>>>> some it is actually enforced. For example, I do quite a bit of work 
> > >>>>> with classifiers using the sklearn estimator API, and for various 
> > >>>>> reasons sklearn enforce this pattern for an __init__ (see here if 
> > >>>>> interested).
> > >>>>>
> > >>>>> Here is an example of this pattern from the standard library (from 
> > >>>>> textwrap.TextWrapper):
> > >>>>> ```
> > >>>>> def __init__(self,
> > >>>>>          width=70,
> > >>>>>          initial_indent="",
> > >>>>>          subsequent_indent="",
> > >>>>>          expand_tabs=True,
> > >>>>>          replace_whitespace=True,
> > >>>>>          fix_sentence_endings=False,
> > >>>>>          break_long_words=True,
> > >>>>>          drop_whitespace=True,
> > >>>>>          break_on_hyphens=True,
> > >>>>>          tabsize=8,
> > >>>>>          *,
> > >>>>>          max_lines=None,
> > >>>>>          placeholder=' [...]'):
> > >>>>> self.width = width
> > >>>>> self.initial_indent = initial_indent
> > >>>>> self.subsequent_indent = subsequent_indent
> > >>>>> self.expand_tabs = expand_tabs
> > >>>>> self.replace_whitespace = replace_whitespace
> > >>>>> self.fix_sentence_endings = fix_sentence_endings
> > >>>>> self.break_long_words = break_long_words
> > >>>>> self.drop_whitespace = drop_whitespace
> > >>>>> self.break_on_hyphens = break_on_hyphens
> > >>>>> self.tabsize = tabsize
> > >>>>> self.max_lines = max_lines
> > >>>>> self.placeholder = placeholder
> > >>>>> ```
> > >>>>>
> > >>>>> With a quick scan of the top 50 or so most used python packages, 1 in 
> > >>>>> 4 __init__ methods that takes arguments has the line `self.argument_i 
> > >>>>> = argument_i` for every single argument, with several of them having 
> > >>>>> 10+ arguments.
> > >>>>>
> > >>>>> 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
> > >>>>> ```
> > >>>>>
> > >>>>> This could alternatively be implemented as a decorator, like so
> > >>>>> ```
> > >>>>> @assign
> > >>>>> def __init__(self, argument_1, argument_2, argument_3=None):
> > >>>>>     # other init logic goes here
> > >>>>> ```
> > >>>>> but then this requires a `pass` if no other logic is needed inside 
> > >>>>> the __init__. There may also be some other syntax for this which 
> > >>>>> would be even easier to use.
> > >>>>>
> > >>>>> Is this something that others would find useful?
> > >>>>>
> > >>>>> Thanks,
> > >>>>>
> > >>>>> Lewis
> > >>>>> _______________________________________________
> > >>>>> 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/VLI3DOFA5VWMGJMJGRDC7JZTRKEPPZNU/
> > >>>>> Code of Conduct: http://python.org/psf/codeofconduct/
> > >>>
> > >>> _______________________________________________
> > >>> 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/SCTXSEKOWDRDGVXXOEB7JUC6WE7XKGMO/
> > >>> Code of Conduct: http://python.org/psf/codeofconduct/
> > >>
> > >> _______________________________________________
> > >> 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/3QY6NIT7Y37PHKCYGJXJAONS35E3YZWH/
> > >> Code of Conduct: http://python.org/psf/codeofconduct/
> > >
> > > _______________________________________________
> > > 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/4SQF4E3XWQ4XKAS3DMTNYJYJBZEUQIKC/
> > > Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
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/6CJNOJPLPLTBOSZ6QNK5APPINRZ4PORO/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to