__init__ определение функции без аргумента self

Пробираясь через класс Counter python в collections, я обнаружил, что мне показалось странным: они явно не используют аргумент self аргументах функции __init__.

См. Код ниже (скопировано напрямую без строки документации):

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)

Позже в этом же классе методы update и subtract также определяются таким же образом.

Прежде чем указать мне на вопросы о том, как self работает в классах, отмечу, что я не считаю, что это дубликат вопрос. Я понимаю, как обычно работает self и что self не является ключевым словом (просто стандартная практика) и т.д. Я также понимаю, что этот код работает (я не подвергаю сомнению правильность синтаксиса * unpack/explode/starred-expressions)

Мой вопрос больше связан с тем, почему...

  • Зачем реализовывать __init__ и другие нормальные (non- @static/@class методы) такого класса и при каких обстоятельствах я должен рассмотреть возможность использования этого в будущем?
  • Почему только определенные методы в одном классе реализованы таким образом?
  • При каких обстоятельствах эти методы будут вызываться без аргументов (если они есть), инициирующих первую ошибку TypeError?
  • В каких условиях эти методы можно назвать с self заполненной вручную (например, Counter.__init__(some_counter))? Или другие примеры?

Я должен думать, что это как-то связано с TypeError ("дескриптор...").

Ответ 1

Этот код предназначен для self позиционирования. В противном случае вызов как

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

потерпит неудачу из-за получения __init__ двух значений self.

Большинству классов не требуется ничего подобного этой специальной обработке, но Counter должен обрабатывать аргументы ключевых слов, как это делает dict, где они становятся ключами результирующего отображения, даже если ключ 'self'. Другие методы Counter которые имеют такую обработку, - те, которые требуют того же ключевого поведения аргумента.

Если вам нужно рассматривать self как действительный аргумент ключевого слова в своем собственном коде, вы, вероятно, должны сделать что-то подобное.


Что касается TypeError, что в соответствии с сообщением об ошибке от 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

Наиболее вероятный способ Counter.__init__ это на практике - это, вероятно, люди, Counter.__init__ подклассы Counter и забывающие передать self в Counter.__init__ (или использовать super).