Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__init__ function definition without self argument

Tags:

python

In digging through the python Counter class in collections, I found something I thought was peculiar: They don't explicitly use the self argument in the __init__ function's arguments.

See code below (copied directly without the docstring):

class Counter(dict):
    def __init__(*args, **kwds):
        if not args:
            raise TypeError("descriptor '__init__' of 'Counter' object "
                            "needs an argument")
        self, *args = args
        if len(args) > 1:
            raise TypeError('expected at most 1 argments, got %d' % len(args))
        super(Counter, self).__init__()
        self.update(*args, **kwds)

Later in this same class, the update and subtract methods are also defined this same way.

Before you point me to questions about how self works in classes, I will note that I don't believe this is a duplicate question. I understand how self works typically and that self is not a keyword (just standard practice) etc. I also understand that this code works (I'm not questioning the validity of the * unpack/explode/starred-expressions syntax)

My question is more related to why...

  • Why would one implement the __init__ and other normal (non-@static/@class methods) of a class like this and in what circumstances should I consider using this in the future?
  • Why are only specific methods on the same class implemented like this?
  • Under what circumstance would these methods be called without any args (if any), triggering the first TypeError?
  • In what circumstances would these methods be called with self filled in manually (e.g. Counter.__init__(some_counter))? Or other examples?

I have to think that it has something to do with the TypeError("descriptor...").


Edit 11/2020:

In Python 3.8, PEP570 introduces Positional-Only Parameters, eliminating the need to use (*args, **kwds) in this way. This can be seen in the Python 3.8 Counter code:

def __init__(self, iterable=None, /, **kwds):
like image 882
Stephen C Avatar asked Dec 14 '18 22:12

Stephen C


People also ask

Does init have to have self?

__init__ does act like a constructor. You'll need to pass "self" to any class functions as the first argument if you want them to behave as non-static methods.

Can a class exist without __ Init__ function?

You can still instantiate a class that doesn't specify the __init__ method. Leaving it out does not make your class abstract.

What happens if Self is not used in Python?

This is nothing more than a convention: the name self has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention.

Is __ init __ mandatory in Python?

No, it isn't necessary.


1 Answers

This code is intended to make self positional-only. Otherwise, a call like

d = {'self': 5}
Counter(**d)

would fail due to __init__ receiving two values of self.

Most classes don't need anything like this special handling, but Counter is supposed to handle keyword arguments like dict does, where they become keys of the resulting mapping, even if the key is 'self'. The other Counter methods that have this handling are the ones that need the same keyword argument behavior.

If you need to treat self as a valid keyword argument in your own code, you should probably do something similar.


As for the TypeError, that's there to match the error message from dict.__init__:

>>> dict.__init__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__init__' of 'dict' object needs an argument
>>> Counter.__init__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/collections/__init__.py", line 560, in __init__
    raise TypeError("descriptor '__init__' of 'Counter' object "
TypeError: descriptor '__init__' of 'Counter' object needs an argument

The most likely way for this to come up in practice is probably people subclassing Counter and forgetting to pass self to Counter.__init__ (or use super).

like image 113
user2357112 supports Monica Avatar answered Oct 13 '22 22:10

user2357112 supports Monica