__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
).