2009-09-15 2 views
6

Я видел несколько вопросов по этой теме, но я не смог найти окончательного ответа.Как вы смешиваете классы Python в стиле старого стиля и нового стиля?

Я хотел бы знать, как правильно использовать классы старого стиля в новой базе кода Python. Скажем, например, что у меня есть два фиксированных класса, A и B. Если я хочу создать подкласс и B и конвертировать в новых классов (A2 и B2), это работает. Однако есть проблема, если я хочу создать новый класс C, от A2 и B2.

Таким образом, можно ли продолжить этот метод или все классы должны соответствовать старому стилю, если какой-либо базовый класс определен как старомодный?

Смотрите пример кода для осветления:

class A: 
    def __init__(self): 
     print 'class A' 

class B: 
    def __init__(self): 
     print 'class B' 

class A2(A,object): 
    def __init__(self): 
     super(A2, self).__init__() 
     print 'class A2' 

class B2(B,object): 
    def __init__(self): 
     super(B2, self).__init__() 
     print 'class B2' 

class C(A2, B2): 
    def __init__(self): 
     super(C,self).__init__() 
     print 'class C' 

A2() 
print '---' 
B2() 
print '---' 
C() 

Выход этого кода:

class A 
class A2 
--- 
class B 
class B2 
--- 
class A 
class A2 
class C 

Как вы можете видеть, проблема заключается в том, что в вызове C(), класс B2 никогда не инициализировался.


Update - New-Style Класс Пример

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

class A(object): 
    def __init__(self): 
     super(A, self).__init__() 
     print 'class A' 

class B(object): 
    def __init__(self): 
     super(B, self).__init__() 
     print 'class B' 

class A2(A): 
    def __init__(self): 
     super(A2, self).__init__() 
     print 'class A2' 

class B2(B): 
    def __init__(self): 
     super(B2, self).__init__() 
     print 'class B2' 

class C(A2, B2): 
    def __init__(self): 
     super(C, self).__init__() 
     print 'class C' 

C() 

и производит вывод:

class B 
class B2 
class A 
class A2 
class C 
+0

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

+0

Почему у вас вообще есть классы старого стиля? –

+0

Возможно, сторонние модули? –

ответ

6

Это не проблема смешения старых и новых классов стилей. super() не вызывает все функции базовых классов, он называет первый найденный в соответствии с порядком разрешения метода. В этом случае А2, который, в свою очередь, вызывает A.

Если вы хотите вызвать и другое, сделать это явно:

class C(A2, B2): 
    def __init__(self): 
     A2.__init__(self) 
     B2.__init__(self) 
     print 'class C' 

Это должно решить эту проблему.

Update:

Проблема алмазов наследования, как вы смотрите, это вопрос о том, какой класс назвать в ситуации алмаз наследования, как это:

class A: 
    def method1(self): 
     print 'class A' 

    def method2(self): 
     print 'class A' 

class B(A): 
    def method1(self): 
     print 'class B' 

class C(A): 
    def method1(self): 
     print 'class C' 

    def method2(self): 
     print 'class C' 

class D(B, C): 
    pass 

Теперь проверить это:

>>> D().method1() 
'class B' 

Это правильно. Он вызывает реализацию первого класса. Однако, давайте попробуем это с method2:

>>> D().method2() 
'class A' 

Oups, НЕПРАВИЛЬНО! Он должен был вызвать класс C.method2() здесь, потому что, хотя класс B не переопределяет метод2, класс C делает. Теперь сделайте класс Аа Newstyle класс:

class A(object): 
    def method1(self): 
     print 'class A' 

И попробуйте еще раз:

>>> D().method1() 
'class B' 
>>> D().method2() 
'class C' 

и вуаля, он работает. Это разность ордеров на разрешение метода между классами нового и старого стиля, и это то, что иногда заставляет смешивать их.

Обратите внимание, как ни в коем случае не вызываются как B, так и C. Это верно, даже если мы называем супер.

class D(B, C): 
    def method1(self): 
     super(D, self).method1() 

    def method2(self): 
     super(D, self).method2() 

>>> D().method1() 
'class B' 
>>> D().method2() 
'class C' 

Если вы хотите позвонить как B, так и C, вы ДОЛЖНЫ вызывать оба явно.

Теперь, если вы Unbreak алмаз, как в вашем примере, имеющие разные базовые классы, результат отличается:

class A1(object): 
    def method1(self): 
     print 'class A1' 

    def method2(self): 
     print 'class A1' 

class A2(object): 
    def method1(self): 
     print 'class A2' 

    def method2(self): 
     print 'class A2' 

class B(A1): 
    def method1(self): 
     print 'class B' 

class C(A2): 
    def method1(self): 
     print 'class C' 

    def method2(self): 
     print 'class C' 

class D(B, C): 
    def method1(self): 
     super(D, self).method1() 

    def method2(self): 
     super(D, self).method2() 


>>> D().method1() 
'class B' 
>>> D().method2() 
'class A1' 

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

+0

Супер вызовет все методы базового класса, если они будут реализованы со всеми классами нового стиля и будут использоваться последовательно. Мой вопрос заключается в том, как обращаться с этим случаем, когда вы вынуждены смешиваться в классе старого стиля. Я знаю, как напрямую обращаться к родительским методам, но это ломается для наследования бриллиантов. – cmcginty

+0

Нет, не будет. Если вы делаете классы нового стиля A и B, вы получаете точно такой же результат, как и выше. И я не вижу, как это сломает разрывное наследование алмаза, чтобы напрямую вызвать вызов родительских методов. Я подозреваю, что у вас проблема наследования алмазов немного назад. На самом деле, похоже, вы предполагаете, что наследование бриллиантов должно вызывать оба базовых класса (чего обычно нет), и единственный способ сделать это в Python - это явно их называть. –

+0

Смотрите мое обновление, супер выполняет все \ __ init \ __ методы. – cmcginty