The problem of assigning init arguments as attributes has appeared several times in the past (https://mail.python.org/archives/list/python-ideas@python.org/message/VLI3DOFA5VWMGJMJGRDC7JZTRKEPPZNU/ was the most recent we could find) and is already handled in dataclasses.


Lately, discussing this topic with a friend, we thought that using a specific token could be a possible approach, so you could do:


class MyClass:

    def __init__(self, @a, @b, c):

        pass


and it would be analogous to doing:


class MyClass:

    def __init__(self, a, b, c):

        self.a = a

        self.b = b


Then, you would instantiate the class as usual, and the variables tagged with `@` would be bound to the object:


>>> objekt = MyClass(2, 3, 4)

>>> print(objekt.b)

3

>>> print(objekt.c)

AttributeError: 'MyClass' object has no attribute 'c'



We have a working implementation here if anyone wants to take a look at: https://github.com/pabloalcain/cpython/tree/feature/auto_attribute. Keep in mind that we have limited knowledge about how to modify cpython itself, and which would the best places be to do the modifications, so it's more than likely that some design decisions aren't very sound (https://devguide.python.org/grammar/ and https://devguide.python.org/parser/ were incredibly helpful).


Besides the implementation, we would like to know what the community thinks on whether this might have any value. While developing this, we realized that Crystal already has this feature (eg https://github.com/askn/crystal-by-example/blob/master/struct/struct.cr) with the same syntax; which is kind of expected, considering it's syntax is based on Ruby.



Random collection of thoughts:


1. If auto-assignment made sense in general, one of the reasons we went for this rather than the decorator approach is that we wouldn't like to have a list of strings that can vary decoupled from the actual argument name.


2. The current implementation of `@` works for any function, not only init. We don't know if this would actually be a desirable feature.


3. It also works with any function in the wild. This mostly allows for monkey-patching to work out of the box:


>>> class Klass:

...     def __init__(self):

...         pass

... 

>>> def add_parameter(k, @p):

...     pass

... 

>>> Klass.add_parameter = add_parameter

>>> objekt = Klass()

>>> print(objekt.p)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

AttributeError: 'Klass' object has no attribute 'p'

>>> objekt.add_parameter(11)

>>> print(objekt.p)

11


Again, we are not sure if this is desirable, but it's what made most sense for us at the moment.


4. Adding the `@` token to the argument doesn’t remove the variable from the function/method scope, so this would be perfectly valid:


>>> def my_function(k, @parameter):

...     print(parameter)

>>> my_function(objekt, 4)

4

>>> k.parameter

4




5. We didn’t implement it for lambda functions.


Cheers,

Pablo and Quimey