Как бы то ни было, ошибка также возникает, когда файл cpp, включенный в проект, не существует.
Если вы перечислите исходные файлы в CMakeLists.txt и ошибочно введите имя файла, тогда вы получите эту ошибку.
Преимущества super()
в одиночном наследовании минимальны - в основном вам не нужно жестко закодировать имя базового класса во все методы, которые используют его родительские методы.
Однако использовать множественное наследование практически невозможно super()
. Это включает в себя общие идиомы, такие как mixins, интерфейсы, абстрактные классы и т. Д. Это распространяется на код, который позже расширяет ваши. Если кто-то позже захотел написать класс, который расширил Child
и mixin, их код не будет работать должным образом.
Я немного поиграл с super()
и узнал, что мы можем изменить порядок вызова.
Например, у нас есть следующая структура иерархии:
A
/ \
B C
\ /
D
In этот случай MRO of D будет (только для Python 3):
In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)
Давайте создадим класс, где super()
вызывает после выполнения метода.
In [23]: class A(object): # or with Python 3 can define class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: print("I'm from B")
...: super().__init__()
...:
...: class C(A):
...: def __init__(self):
...: print("I'm from C")
...: super().__init__()
...:
...: class D(B, C):
...: def __init__(self):
...: print("I'm from D")
...: super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A
A
/ ⇖
B ⇒ C
⇖ /
D
Таким образом, мы можем видеть, что порядок разрешения такой же, как в MRO. Но когда мы назовем super()
в начале метода:
In [21]: class A(object): # or class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: super().__init__() # or super(B, self).__init_()
...: print("I'm from B")
...:
...: class C(A):
...: def __init__(self):
...: super().__init__()
...: print("I'm from C")
...:
...: class D(B, C):
...: def __init__(self):
...: super().__init__()
...: print("I'm from D")
...: d = D()
...:
I'm from A
I'm from C
I'm from B
I'm from D
У нас есть другой порядок, он меняет порядок кортежа MRO.
A
/ ⇘
B ⇐ C
⇘ /
D
Для Дополнительное чтение Я бы рекомендовал следующие ответы:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
Это довольно легко понять.
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
Хорошо, что происходит сейчас, если вы используете super(Child,self)
?
Когда создается экземпляр Child, его MRO (порядок разрешения метода) находится в порядке (Child, SomeBaseClass, object) на основе наследования. (предположим, что у SomeBaseClass нет других родителей, кроме объекта по умолчанию).
Передавая Child, self
, super
выполняет поиск в MRO экземпляра self
и возвращает прокси-объект next of Child , в этом случае это SomeBaseClass, этот объект затем вызывает метод __init__
для SomeBaseClass. Другими словами, если это super(SomeBaseClass,self)
, то прокси-объект, который возвращает super
, будет object
. Для множественного наследования MRO может содержать много классов, поэтому в основном super
позволяет вам решить, где вы хотите начать поиск в MRO.
Не все ли это предполагают, что базовый класс является классом нового стиля?
class A:
def __init__(self):
print("A.__init__()")
class B(A):
def __init__(self):
print("B.__init__()")
super(B, self).__init__()
Не будет работать в Python 2. class A
должен быть в стиле new, т. е. class A(object)
При вызове super()
для решения родительской версии метода класса, метода экземпляра или метода static мы хотим передать текущий класс, объем которого мы находимся в качестве первого аргумента, чтобы указать область действия родителя, которую мы пытаемся чтобы разрешить и в качестве второго аргумента объект интереса указать, к какому объекту мы пытаемся применить эту область.
Рассмотрим иерархию классов A
, B
и C
где каждый класс является родителем следующего за ним и a
, b
и c
соответствующих экземпляров каждого.
super(B, b)
# resolves to the scope of B's parent i.e. A
# and applies that scope to b, as if b was an instance of A
super(C, c)
# resolves to the scope of C's parent i.e. B
# and applies that scope to c
super(B, c)
# resolves to the scope of B's parent i.e. A
# and applies that scope to c
super
со статическим методом , например используя super()
из метода __new__()
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
Объяснение:
1-, хотя обычно __new__()
обычно принимает в качестве первого параметра ссылку на вызывающий класс, это not , реализованный в Python как classmethod, а скорее статический метод. То есть ссылка на класс должна быть явно передана в качестве первого аргумента при непосредственном вызове __new__()
:
# if you defined this
class A(object):
def __new__(cls):
pass
# calling this would raise a TypeError due to the missing argument
A.__new__()
# whereas this would be fine
A.__new__(A)
2- при вызове super()
для перехода к родительскому классу мы передаем child class A
в качестве своего первого аргумента, тогда мы передаем ссылку на объект, представляющий интерес, в этом случае это ссылка на класс, которая была передана при вызове A.__new__(cls)
. В большинстве случаев это также ссылка на дочерний класс. В некоторых ситуациях это может быть не так, например, в случае наследования с несколькими поколениями.
super(A, cls)
3-, поскольку поскольку общее правило __new__()
является статическим методом, super(A, cls).__new__
также возвращает статический метод и должен быть предоставлен все аргументы явно, включая ссылку на объект insterest, в этом случае cls
.
super(A, cls).__new__(cls, *a, **kw)
4- делать то же самое без super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
super
с помощью метода экземпляра , например используя super()
из __init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Объяснение:
1- __init__
- это метод экземпляра, означающий, что в качестве первого аргумента он принимает ссылку на пример. При вызове непосредственно из экземпляра ссылка передается неявно, то есть вам не нужно указывать ее:
# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...
# you create an instance
a = A()
# you call `__init__()` from that instance and it works
a.__init__()
# you can also call `__init__()` with the class and explicitly pass the instance
A.__init__(a)
2- при вызове super()
в __init__()
мы передаем дочерний класс как первый аргумент и объект интереса как второй аргумент, который в общем случае является ссылкой на экземпляр дочернего класса.
super(A, self)
3- Вызов super(A, self)
возвращает прокси-сервер, который будет разрешите область действия и примените ее к self
, как если бы она стала экземпляром родительского класса. Назовем этот прокси s
. Поскольку метод __init__()
является методом экземпляра, вызов s.__init__(...)
будет неявно передавать ссылку self
в качестве первого аргумента родительскому __init__()
.
4- сделать то же самое без super
нам нужно передать ссылку на экземпляр явно в родительскую версию __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
super
с помощью метода класса class A(object):
@classmethod
def alternate_constructor(cls, *a, **kw):
print "A.alternate_constructor called"
return cls(*a, **kw)
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return super(B, cls).alternate_constructor(*a, **kw)
Объяснение:
1- Метод класса можно вызывать непосредственно из класса и в качестве его первого параметра берет ссылку на класс.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- при вызове super()
в классе-методе, чтобы разрешить его родительскую версию, мы хотим передать текущий дочерний класс в качестве первого аргумента, чтобы указать, какую область действия родителя мы пытаемся разрешить и объект интереса в качестве второго аргумента, чтобы указать, к какому объекту мы хотим применить эту область действия, которая в общем случае является ссылкой на сам дочерний класс или один из его подклассов.
super(B, cls_or_subcls)
3- Вызов super(B, cls)
разрешает область действия A
и применяет ее к cls
. Поскольку alternate_constructor()
является методом класса, вызов super(B, cls).alternate_constructor(...)
будет неявно передавать ссылку cls
в качестве первого аргумента A
alternate_constructor()
super(B, cls).alternate_constructor()
[4] для выполнения то же самое без использования super()
вам нужно будет получить ссылку на unbound версию A.alternate_constructor()
(то есть на явную версию функции). Просто это не сработает:
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return A.alternate_constructor(cls, *a, **kw)
Вышеупомянутое не будет работать, потому что метод A.alternate_constructor()
принимает в качестве первого аргумента неявную ссылку на A
. Здесь cls
будет вторым аргументом.
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
# first we get a reference to the unbound
# `A.alternate_constructor` function
unbound_func = A.alternate_constructor.im_func
# now we call it and pass our own `cls` as its first argument
return unbound_func(cls, *a, **kw)