"__init__" looks so ugly in Python. Is there another way to code this construct?

Viewed 908

I have used this in my code, but "underscore underscore init underscore underscore" looks so ugly in Python, because it is not clear at a glance as to whether these prefixes and suffixes are one, two or three characters. Is there another way to code this construct without the double underscore? For example:

class MyForm(forms.Form):
    foo = forms.CharField()
    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.bar = bar(self)

Annoyingly this code does not work, so obviously the underscore plays an essential role:

class MyForm(forms.Form):
    foo = forms.CharField()
    def init(self, *args, **kwargs):
        super(MyForm, self).init(*args, **kwargs)
        self.bar = bar(self)

What is the purpose of this syntax?

2 Answers

Is there another way to code this construct without the double underscore?

No, that's part of the Python object model directly:

https://docs.python.org/3/reference/datamodel.html#object.__init__

You can not change this without recompiling your own Python interpreter.

What is the purpose of this syntax?

The double underscore "dunder" is there to indicate a method which has special meaning in the object model. In time you will get used to it and may even begin to like it. When reading code, it provides an easy way to visually distinguish the Python datamodel "hooks" in any 3rd-party code.

Although I completely agree with @wim's answer, you can achieve what you want by using a decorator:

def custom_init(name='init'):
    def wrapper(callable):
        if not hasattr(callable, name):
            raise AttributeError('Attribute {} not found on {}'.format(name, callable))
        callable.__init__ = getattr(callable, name)
        return callable
    return wrapper

You would then just:

@custom_init(name='foo')
class SomeClass(object):
    def foo(self, some_value):
        self.value = some_value

c = SomeClass(42)
print(c.value) # Prints 42

Of course, for derived classes, you just need to remember to call __init__ on the base class:

@custom_init()
class MyForm(forms.Form):
    foo = forms.CharField()
    def init(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.bar = bar(self)
Related