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

Reply via email to