Следующий код:
>>> 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)
. Но я делаю это намеренно. Я просто не могу понять сообщение об ошибке, которое указывает, что некоторые процедуры существуют при наследовании классов, я хочу знать, какая процедура вызывает это.
Я действительно не понимаю, в чем ваша путаница - создание экземпляра класса Foo
не вызывает исключение, а передает этот экземпляр классу Spam
, который есть.
@OuterSoda Упомянутая конкретная ошибка очень сбивает с толку. По какой-то причине Python действительно создает новый экземпляр класса Foo
.
@OuterSoda Да, но почему сообщение об ошибке «Foo() не принимает аргументов», а не что-то вроде «TypeError: Foo() не является типом, который можно использовать в качестве базового класса»
ах, это твоя проблема. Это потому, что у Foo нет функции инициализации (__init__
), как вы только что написали pass. Я предполагал, что вы замените его реальным классом.
Вы не можете наследовать объект/экземпляр. Но вы можете сделать это:
>>> 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
, но, видимо, это неправильно)
@jonrsharpe Я обновляю свое описание. Ваш способ может устранить ошибку, но не может устранить мое замешательство.