Как вы смешиваете классы Python старого и нового стиля? - PullRequest
7 голосов
/ 15 сентября 2009

Я видел несколько вопросов по этой теме, но не смог найти однозначного ответа.

Я хотел бы знать, как правильно использовать классы старого стиля в новой базе кода Python. Скажем, например, что у меня есть два фиксированных класса, A и B . Если я хочу создать подкласс A и 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 никогда не инициализировался.


Обновление - пример класса в новом стиле

Полагаю, неясно, какой должна быть правильная последовательность инициализации при использовании super. Вот рабочий пример, когда вызов 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

1 Ответ

6 голосов
/ 15 сентября 2009

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

Если вы хотите вызвать оба, сделайте это явно:

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

Это должно решить это.

Обновление:

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

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'

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

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, вы ДОЛЖНЫ вызывать оба явно.

Теперь, если вы откроете бриллиант, как в вашем примере с отдельными базовыми классами, результат будет другим:

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'

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

...