Как интерпретировать сообщение об ошибке «Foo() не принимает аргументов» при указании экземпляра класса в качестве базового класса?

Следующий код:

>>> class Foo: pass
>>> class Spam(Foo()): pass

конечно вызовет сообщение об ошибке:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Foo() takes no arguments

Но информация об ошибке немного странная. Кажется, что есть какие-то процессы при инициализации класса Spam.

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


Примечание: я знаю, что не будет ошибки, если напишите так class Spam(Foo). Но я делаю это намеренно. Я просто не могу понять сообщение об ошибке, которое указывает, что некоторые процедуры существуют при наследовании классов, я хочу знать, какая процедура вызывает это.

@jonrsharpe Я обновляю свое описание. Ваш способ может устранить ошибку, но не может устранить мое замешательство.

yixuan 14.04.2023 11:55

Я действительно не понимаю, в чем ваша путаница - создание экземпляра класса Foo не вызывает исключение, а передает этот экземпляр классу Spam, который есть.

OuterSoda 14.04.2023 11:56

@OuterSoda Упомянутая конкретная ошибка очень сбивает с толку. По какой-то причине Python действительно создает новый экземпляр класса Foo.

MegaIng 14.04.2023 12:00

@OuterSoda Да, но почему сообщение об ошибке «Foo() не принимает аргументов», а не что-то вроде «TypeError: Foo() не является типом, который можно использовать в качестве базового класса»

mkrieger1 14.04.2023 12:00

ах, это твоя проблема. Это потому, что у Foo нет функции инициализации (__init__), как вы только что написали pass. Я предполагал, что вы замените его реальным классом.

OuterSoda 14.04.2023 12:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
5
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы не можете наследовать объект/экземпляр. Но вы можете сделать это:

>>> class Foo: ...
...
>>> foo = Foo()
>>> class Spam(foo.__class__): ...
...
>>> spam = Spam()
>>> isinstance(spam, Foo)
True
>>>
Ответ принят как подходящий

Используя несколько операторов грязной печати, мы можем получить последовательность происходящих событий:

class Foo:
    def __init__(self, *args, **kwargs):
        print("__init__", self, args, kwargs)

    def __new__(cls, *args, **kwargs):
        print("__new__", cls, args, kwargs)
        return super().__new__(cls)

inst = Foo()

class Spam(inst): pass

print(inst, Spam)

выходы

__new__ <class '__main__.Foo'> () {}
__init__ <__main__.Foo object at 0x0000019FFB399550> () {}
__new__ <class '__main__.Foo'> ('Spam', (<__main__.Foo object at 0x0000019FFB399550>,), {'__module__': '__main__', '__qualname__': 'Spam'}) {}
__init__ <__main__.Foo object at 0x0000019FFB399590> ('Spam', (<__main__.Foo object at 0x0000019FFB399550>,), {'__module__': '__main__', '__qualname__': 'Spam'}) {}
<__main__.Foo object at 0x0000019FFB399550> <__main__.Foo object at 0x0000019FFB399590>

Таким образом, попытка наследования от экземпляра заставляет Python создавать новый экземпляр этого класса, присваивая ему параметры, которые обычно передаются в type.__new__.

Причина, по которой это происходит, заключается в том, что типы в python являются объектами: при создании класса python пытается получить правильный метакласс, вызывая type(base), который в этой ситуации возвращает Foo (класс), а затем пытается создать экземпляр этого . (Я мог бы поклясться, что Python проверяет, что type(base) является подклассом type, но, видимо, это неправильно)

Другие вопросы по теме