Подтвердить что ты не робот

Почему super() разбит на Python-2.x?

Часто указывалось, что super должно быть avoided в Python 2. Я нашел в моем использовании super в Python 2, что он никогда не действует так, как я ожидаю, если я не предоставил все аргументы, такие как пример:

super(ThisClass, self).some_func(*args, **kwds)

Мне кажется, что это побеждает цель использования super(), это не более кратким или намного лучше, чем TheBaseClass.some_func(self, *args, **kwds). Для большинства целей порядок разрешения порядка - далекая сказка.

4b9b3361

Ответ 1

super() не разбивается - он просто не должен рассматриваться как стандартный способ вызова метода базового класса. Это не изменилось с Python 3.x. Единственное, что изменилось, это то, что вам не нужно передавать аргументы self, cls в стандартном случае, когда self является первым параметром текущей функции, а cls - это класс, который в настоящее время определяется.

Что касается вашего вопроса, когда на самом деле использовать super(), мой ответ будет: вряд ли когда-либо. Я лично стараюсь избегать типа множественного наследования, которое сделало бы super() полезным.

Изменить: пример из реальной жизни, с которым я когда-то сталкивался: у меня были некоторые классы, определяющие метод run(), некоторые из которых имели базовые классы. Я использовал super() для вызова унаследованных конструкторов - я не думал, что это важно, потому что я использовал только одно наследование:

class A(object):
    def __init__(self, i):
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, i, j):
        super(B, self).__init__(i)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

Только представьте, что было несколько из этих классов, все с отдельными прототипами конструкторов и все с тем же интерфейсом с run().

Теперь я хотел добавить некоторые дополнительные функции ко всем этим классам, скажем, протоколирование. Дополнительные функции потребовали дополнительного метода для всех классов, например info(). Я не хотел вторгаться в исходные классы, а скорее определял второй набор классов, наследующих от исходных, добавляя метод info() и наследуя от микширования, обеспечивающего фактическое ведение журнала. Теперь я больше не мог использовать super() в конструкторе, поэтому я использовал прямые вызовы:

class Logger(object):
    def __init__(self, name):
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, i, j):
        B.__init__(self, i, j)
        Logger.__init__("B")
    def info(self):
        return 42

Здесь перестают работать. Вызов super() в конструкторе базового класса неожиданно вызывает Logger.__init__(), а BLogged ничего не может с этим поделать. На самом деле нет способа выполнить эту работу, за исключением удаления вызова super() в B.

[ Другое Редактирование. Я, кажется, не сделал свое мнение, судя по всем комментариям здесь и ниже других ответов. Вот как сделать этот код с помощью super():

class A(object):
    def __init__(self, i, **kwargs):
        super(A, self).__init__(**kwargs)
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, j, **kwargs):
        super(B, self).__init__(**kwargs)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

class Logger(object):
    def __init__(self, name, **kwargs):
        super(Logger,self).__init__(**kwargs)
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, **kwargs):
        super(BLogged, self).__init__(name="B", **kwargs)
    def info(self):
        return 42

b = BLogged(i=3, j=4)

Сравните это с использованием явных вызовов суперкласса. Вы решаете, какую версию вы предпочитаете.]

Это и подобные истории объясняют, почему я считаю, что super() не следует рассматривать как стандартный способ вызова методов базового класса. Это не означает, что нарушается super().

Ответ 2

super() не разбит на Python 2 или Python 3.

Рассмотрим аргументы из сообщения в блоге:

  • Это не так, как кажется.

Хорошо, вы можете согласиться или не согласиться на это, это довольно субъективно. Что тогда нужно было назвать? super() является заменой для прямого вызова суперкласса, поэтому имя кажется мне прекрасным. Он НЕ называет суперкласс непосредственно, потому что, если бы это было все, что было, это было бы бессмысленно, так как вы все равно могли бы это сделать. ОК, по общему признанию, это может быть не очевидно, но случаи, когда вам нужно super(), как правило, не очевидны. Если вам это нужно, вы делаете довольно волосатое многократное наследование. Это не будет очевидно. (Или вы делаете простой mixin, и в этом случае он будет довольно очевиден и будет вести себя так, как вы ожидаете, даже если вы не читали документы).

Если вы можете напрямую вызвать суперкласс, это, вероятно, то, что вы в конечном итоге сделаете. Это легкий и интуитивный способ сделать это. super() только вступает в игру, если это не работает.

  • Он не очень хорошо связан с вызовом суперкласса.

Да, потому что он предназначен для решения проблемы с этим. Вы можете напрямую вызвать суперкласс, если и только если вы точно знаете, какой класс. Например, вы не используете миксины или когда иерархия классов настолько запутана, что вы фактически объединяете две ветки (что является типичным примером во всех примерах использования super()).

Итак, если каждый класс в иерархии классов имеет четко определенное место, вызывается непосредственный вызов суперкласса. Если вы этого не сделаете, то это не сработает, и в этом случае вы должны использовать super(). То, что точка super() показывает, что "следующий суперкласс" соответствует MRO, без явного указания его, потому что вы не всегда можете это сделать, потому что вы не всегда знаете, что это такое, например, при использовании миксинов.

  • Полностью другой язык программирования Dylan, своего рода lisp -thingy, решает это по-другому, который нельзя использовать в Python, потому что он очень отличается.

Э. OK?

  • super() не вызывает ваш суперкласс.

Да, ты это сказал.

  • Не смешивайте super() и прямой вызов.

Да, ты тоже это сказал.

Итак, против него есть два аргумента: 1. Плохое имя. 2. Вы должны использовать его последовательно.

Это не означает, что он "сломан" или что его следует "избегать".

Ответ 3

Вы, кажется, подразумеваете в своем посте, что

def some_func(self, *args, **kwargs):
    self.__class__.some_func(self, *args, **kwargs)

не является бесконечной рекурсией. Это так, и супер было бы более правильным.

Кроме того, да, вы должны передать все аргументы в super(). Это немного напоминает жалобу, что max() работает не так, как ожидалось, если вы не передадите все номера, которые вы хотите проверить.

В 3.x, однако, требуется меньше аргументов: вы можете сделать super().foo(*args, **kwargs) вместо super(ThisClass, self).foo(*args, **kwargs).


В любом случае, я не уверен в любых ситуациях, когда супер следует избегать. Его поведение только "странно", когда задействован MI, а когда задействован MI, super() - это в основном ваш только надежда на правильное решение. В Single-Inheritance это немного немного словнее SuperClass.foo(self, *args, **kwargs) и ничего не делает.

Я думаю, что я согласен с Свеном в том, что такого рода МИ стоит избежать, но я не согласен с тем, что super стоит избегать. Если ваш класс должен быть унаследован, super предлагает пользователям вашего класса надеяться на то, что MI будет работать, если они такие странные, поэтому он сделает ваш класс более удобным.

Ответ 4

Вы прочитали статью, которую вы связали? Он не делает вывод о том, что super следует избегать, но при его использовании следует опасаться его оговорок. Эти оговорки кратко изложены в статье, хотя я бы не согласился с их предложениями.

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

Если вы не выполняете множественное наследование, super дает вам преимущество в том, что любой, унаследовавший ваш класс, может добавлять простые микшины, а их __init__ будет правильно вызван. Просто помните, что всегда вызывайте __init__ суперкласса, даже когда вы наследуете от object, и передаете ему все остальные аргументы (*a и **kw). Когда вы вызываете другие методы из родительского класса, также используйте super, но на этот раз используйте свою соответствующую подпись, которую вы уже знаете (то есть убедитесь, что у них есть одна и та же подпись во всех классах).

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

Единственное изменение в super в Python 3.x состоит в том, что вам не нужно явно передавать текущий класс и self. Это делает super более привлекательным, потому что использование этого не означает никакого жесткого кодирования как родительского класса, так и текущего класса.

Ответ 5

@Свен Марнах:

Проблема с вашим примером заключается в том, что вы смешиваете явные вызовы суперкласса B.__init__ и Logger.__init__ в Blogged с super() в B. Это не сработает. Либо вы используете все явные вызовы суперкласса, либо используйте super() для всех классов. Когда вы используете super(), вам нужно использовать его для всех классов, включая A, я думаю. Кроме того, в вашем примере, я думаю, вы могли бы использовать явные вызовы суперкласса во всех классах, т.е. Использовать A.__init__ в классе B.

Когда нет наследования бриллиантов, я думаю, что super() не имеет большого преимущества. Проблема заключается, однако, в том, что вы не знаете заранее, если вы попадете в какое-либо наследование алмазов в будущем, поэтому в таком случае было бы разумно использовать super() в любом случае (но затем использовать его последовательно). В противном случае вам придется изменить все классы позднее или столкнуться с проблемами.