В то время как обещания и обратные вызовы хорошо работают во многих ситуациях, боль в задней части выражает нечто вроде:
if (!name) {
name = async1();
}
async2(name);
. В итоге вы пройдете через async1
; проверьте, не определено ли name
или нет, и соответственно вызовите обратный вызов.
async1(name, callback) {
if (name)
callback(name)
else {
doSomething(callback)
}
}
async1(name, async2)
Хотя в в порядке хорошо , это раздражает, когда у вас много подобных случаев и обработка ошибок. Вы можете проверить проект здесь . Fibers
помогает в решении проблемы. var Fiber = require('fibers')
function async1(container) {
var current = Fiber.current
var result
doSomething(function(name) {
result = name
fiber.run()
})
Fiber.yield()
return result
}
Fiber(function() {
var name
if (!name) {
name = async1()
}
async2(name)
// Make any number of async calls from here
}
Это подробно описано с достаточным количеством подробностей самим Гвидо в его сообщении в блоге Order Resolution (включая две предыдущие попытки).
В вашем примере Third()
вызовет First.__init__
. Python ищет каждый атрибут в родителях класса, поскольку они перечислены слева направо. В этом случае мы ищем __init__
. Итак, если вы определите
class Third(First, Second):
...
, Python начнет с просмотра First
, и, если First
не имеет атрибута, тогда он будет смотреть на Second
.
Эта ситуация становится более сложной, когда наследование начинает пересекать пути (например, если First
унаследовано от Second
). Прочтите ссылку выше для более подробной информации, но, в двух словах, Python попытается сохранить порядок, в котором каждый класс появится в списке наследования, начиная с самого дочернего класса.
Так, например, если у вас есть:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First):
def __init__(self):
print "third"
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"
, MRO будет [Fourth, Second, Third, First].
. Кстати: если Python не может найти порядок согласованного метода, он вызовет исключение, вместо этого о возврате к поведению, которое может удивить пользователя.
Отредактировано, чтобы добавить пример двусмысленного MRO:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
print "third"
Если MF Third
будет [First, Second]
или [Second, First]
? Нет очевидного ожидания, и Python вызовет ошибку:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
Редактирование: я вижу, что несколько человек утверждают, что в приведенных выше примерах нет вызовов super()
, поэтому позвольте мне объяснить: точка примеров чтобы показать, как строится MRO. Они не предназначены для печати «first\nsecond \ third» или что-то еще. Вы можете - и должны, конечно, поиграть с примером, добавить super()
звонки, посмотреть, что произойдет, и получить более глубокое понимание модели наследования Python. Но моя цель состоит в том, чтобы держать его простым и показать, как строится MRO. И он построен, как я объяснил:
>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)
Это касается того, как я решил выдать множественное наследование с разными переменными для инициализации и иметь несколько MixIns с тем же вызовом функции. Мне пришлось явно добавлять переменные в переданные ** kwargs и добавлять интерфейс MixIn в качестве конечной точки для супервызов.
Здесь A
- расширяемый базовый класс, а B
и C
- MixIn классы, которые предоставляют функцию f
. A
и B
оба ожидают параметр v
в своих __init__
и C
ожидает w
. Функция f
принимает один параметр y
. Q
наследуется от всех трех классов. MixInF
- это интерфейс mixin для B
и C
.
class A(object):
def __init__(self, v, *args, **kwargs):
print "A:init:v[{0}]".format(v)
kwargs['v']=v
super(A, self).__init__(*args, **kwargs)
self.v = v
class MixInF(object):
def __init__(self, *args, **kwargs):
print "IObject:init"
def f(self, y):
print "IObject:y[{0}]".format(y)
class B(MixInF):
def __init__(self, v, *args, **kwargs):
print "B:init:v[{0}]".format(v)
kwargs['v']=v
super(B, self).__init__(*args, **kwargs)
self.v = v
def f(self, y):
print "B:f:v[{0}]:y[{1}]".format(self.v, y)
super(B, self).f(y)
class C(MixInF):
def __init__(self, w, *args, **kwargs):
print "C:init:w[{0}]".format(w)
kwargs['w']=w
super(C, self).__init__(*args, **kwargs)
self.w = w
def f(self, y):
print "C:f:w[{0}]:y[{1}]".format(self.w, y)
super(C, self).f(y)
class Q(C,B,A):
def __init__(self, v, w):
super(Q, self).__init__(v=v, w=w)
def f(self, y):
print "Q:f:y[{0}]".format(y)
super(Q, self).f(y)
Я хотел немного уточнить ответ безжизненным , потому что, когда я начал читать о том, как использовать super () в иерархии множественного наследования в Python, я не получил его немедленно.
Что вам нужно понять, так это то, что super(MyClass, self).__init__()
предоставляет следующий метод next __init__
в соответствии с используемым алгоритмом упорядочивания разрешения метода (MRO) в контексте полного наследования иерархия .
Эта последняя часть имеет решающее значение для понимания. Рассмотрим снова пример:
class First(object):
def __init__(self):
super(First, self).__init__()
print "first"
class Second(object):
def __init__(self):
super(Second, self).__init__()
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
В соответствии с этой статьей о порядке разрешения метода Гвидо ван Россума вычисляется порядок разрешения __init__
(до Python 2.3) используя «перекос по левому краю по глубине»:
Third --> First --> object --> Second --> object
После удаления всех дубликатов, кроме последнего, получаем:
Third --> First --> Second --> object
Итак, позволяет следить за тем, что происходит, когда мы создаем экземпляр класса Third
, например x = Third()
.
__init__
третьего вызывается первым. __init__
super(Third,
self).__init__()
разрешается __init__
метод First, который вызывается. __init__
Первого super(First, self).__init__()
вызывает __init__
из Во-вторых, потому что это то, что диктует MRO! __init__
of Second super(Second, self).__init__()
вызывает __init__
объекта, что ничего не значит. После этого будет напечатан «второй». super(First, self).__init__()
будет напечатан «первый». super(Third, self).__init__()
будет напечатан «это все». Это объясняет, почему создание экземпляра Third () приводит к:
>>> x = Third()
second
first
that's it
. Алгоритм MRO был улучшен с Python 2.3, чтобы хорошо работать в сложных случаях, но я думаю что использование «перемотки по глубине слева направо» + «удаление дубликатов ожидается для последнего» все еще работает в большинстве случаев (прокомментируйте, если это не так). Обязательно прочитайте сообщение в блоге Guido!
Ваш код и другие ответы являются ошибками. Они пропускают вызовы super()
в первых двух классах, которые необходимы для совместной работы подкласса.
Вот фиксированная версия кода:
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
Вызов super()
находит следующий метод в MRO на каждом шаге, поэтому First и Second также должны иметь его, иначе выполнение останавливается в конце Second.__init__()
.
Это то, что я get:
>>> Third()
second
first
third
О комментарии @ calfzhou , вы можете использовать, как обычно, **kwargs
:
class A(object):
def __init__(self, a, *args, **kwargs):
print("A", a)
class B(A):
def __init__(self, b, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
print("B", b)
class A1(A):
def __init__(self, a1, *args, **kwargs):
super(A1, self).__init__(*args, **kwargs)
print("A1", a1)
class B1(A1, B):
def __init__(self, b1, *args, **kwargs):
super(B1, self).__init__(*args, **kwargs)
print("B1", b1)
B1(a1=6, b1=5, b="hello", a=None)
Результат:
A None
B hello
A1 6
B1 5
Вы также можете использовать их позиционно:
B1(5, 6, b="hello", a=None)
, но вы должны помнить MRO, это действительно запутанно.
Я могу быть немного раздражающим, но я заметил, что люди каждый раз забыли использовать *args
и **kwargs
, когда они переопределяют метод, в то время как это одно из немногих действительно полезных и разумных способов использования этих «магических переменных».
Я хотел бы добавить к , что @Visionscaper говорит вверху:
Third --> First --> object --> Second --> object
В этом случае интерпретатор не отфильтровывает класс объекта, потому что его дублированный, скорее потому что второй появляется в позиции головы и не появляется в позиции хвоста в подмножестве иерархии. Хотя объект появляется только в хвостовых позициях и не считается сильной позицией в алгоритме C3 для определения приоритета.
Линеаризация (mro) класса C, L (C), является
Линеаризированное слияние выполняется путем выбора общих классов, которые отображаются в виде главы списков, а не хвоста, начиная с вопросов порядка (станет ясно ниже)
линеаризация третьего может быть вычислена следующим образом:
L(O) := [O] // the linearization(mro) of O(object), because O has no parents
L(First) := [First] + merge(L(O), [O])
= [First] + merge([O], [O])
= [First, O]
// Similarly,
L(Second) := [Second, O]
L(Third) := [Third] + merge(L(First), L(Second), [First, Second])
= [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2,
= [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
= [Third, First, Second] + merge([O], [O])
= [Third, First, Second, O]
Таким образом, для реализации super () в следующем коде:
class First(object):
def __init__(self):
super(First, self).__init__()
print "first"
class Second(object):
def __init__(self):
super(Second, self).__init__()
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
становится очевидным, как этот метод будет разрешен
Third.__init__() ---> First.__init__() ---> Second.__init__() --->
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"
Это называется проблемой Diamond , страница имеет запись на Python, но, коротко, Python вызовет методы суперкласса слева направо.
class First(object):
def __init__(self, a):
print "first", a
super(First, self).__init__(20)
class Second(object):
def __init__(self, a):
print "second", a
super(Second, self).__init__()
class Third(First, Second):
def __init__(self):
super(Third, self).__init__(10)
print "that's it"
t = Third()
Выход -
first 10
second 20
that's it
Call to Third () определяет init, определенный в третьем. И вызов super в этой процедуре вызывает init, определенный в First. MRO = [Первый, Второй]. Теперь вызов super в init, определенный в First, продолжит поиск MRO и найдет init, определенный во втором, и любой вызов super ударит объект init по умолчанию. Надеюсь, этот пример прояснит концепцию.
Если вы не называете super от First. Цепь останавливается, и вы получите следующий результат.
first 10
that's it
Я понимаю, что это напрямую не отвечает на вопрос super()
, но я считаю, что он достаточно релевантен для совместного использования.
Существует также способ прямого вызова каждого унаследованного класса:
class First(object):
def __init__(self):
print '1'
class Second(object):
def __init__(self):
print '2'
class Third(First, Second):
def __init__(self):
Second.__init__(self)
Просто отметьте, что если вы сделаете это так, вам придется вызывать их вручную, так как я уверен, что First
__init__()
не будет вызываться.
Еще одна не охваченная точка - это параметры для инициализации классов. Поскольку пункт назначения super
зависит от подкласса, единственным хорошим способом передачи параметров является их упаковка. Затем будьте осторожны, чтобы не иметь одинаковое имя параметра с разными значениями.
Пример:
class A(object):
def __init__(self, **kwargs):
print('A.__init__')
super().__init__()
class B(A):
def __init__(self, **kwargs):
print('B.__init__ {}'.format(kwargs['x']))
super().__init__(**kwargs)
class C(A):
def __init__(self, **kwargs):
print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
super().__init__(**kwargs)
class D(B, C): # MRO=D, B, C, A
def __init__(self):
print('D.__init__')
super().__init__(a=1, b=2, x=3)
print(D.mro())
D()
дает:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__
Вызов суперкласса __init__
непосредственно к более прямому назначению параметров заманчиво, но не удается, если есть какой-либо вызов super
в суперклассе и / или изменен MRO, а класс A может быть вызван несколько раз, в зависимости от реализации.
В заключение: совместное наследование и супер и конкретные параметры для инициализации не работают вместе очень хорошо.
Предполагая, что все спускается с object
(вы сами по себе, если это не так), Python вычисляет порядок разрешения метода (MRO) на основе вашего дерева наследования класса. MRO удовлетворяет 3 свойствам:
Если такой порядок не существует, ошибки Python. Внутренняя работа - это C3-линеризация родословных классов. Подробнее читайте здесь: https://www.python.org/download/releases/2.3/mro/
Таким образом, в обоих приведенных ниже примерах это:
Когда метод вызванное, первое вхождение этого метода в MRO - это тот, который вызывается. Любой класс, который не реализует этот метод, пропускается. Любой вызов super
внутри этого метода вызовет следующее вхождение этого метода в MRO. Следовательно, это имеет значение как в том порядке, в котором вы размещаете классы в наследовании, так и там, где вы помещаете вызовы в super
в методах.
super
сначала в каждом методе class Parent(object):
def __init__(self):
super(Parent, self).__init__()
print "parent"
class Left(Parent):
def __init__(self):
super(Left, self).__init__()
print "left"
class Right(Parent):
def __init__(self):
super(Right, self).__init__()
print "right"
class Child(Left, Right):
def __init__(self):
super(Child, self).__init__()
print "child"
Child()
Выходы:
parent
right
left
child
super
последним в каждом методе class Parent(object):
def __init__(self):
print "parent"
super(Parent, self).__init__()
class Left(Parent):
def __init__(self):
print "left"
super(Left, self).__init__()
class Right(Parent):
def __init__(self):
print "right"
super(Right, self).__init__()
class Child(Left, Right):
def __init__(self):
print "child"
super(Child, self).__init__()
Child()
Выходы:
child
left
right
parent