алмазное наследование классов с теми же членами в Python и с супер - PullRequest
2 голосов
/ 23 февраля 2012

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

Метод, который я использовал при написании классов, я узнал от http://rhettinger.wordpress.com/2011/05/26/super-considered-super/, который связан на сайте документации по Python для встроенной функции super

class A(object):
    pass

class B(A):
    def __init__(self, z=None, **kwargs):
        self.z = z
        super(B, self).__init__(**kwargs)
        # super(B, self).__init__(z=z, **kwargs)

class C(A):
    def __init__(self, z=None, **kwargs):
        self.z = z
        super(C, self).__init__(**kwargs)

class D(B, C):
    pass

d = D(z='y')

for arg, value in d.__dict__.iteritems():
    print arg, ':', value

, который дает вывод

z : None

Проблема в том, что метод использования аргументов ключевых слов для обеспечения соответствия сигнатур функций убирает аргумент z из вызова инициализации класса C. Я могу принудительно добавить аргумент обратно в kwargs (см. Закомментированный код), но тогда это приводит к тому, что я не могу создать экземпляр объекта типа B, так как это приведет к вызову init объекта, который не принимает параметров, это хорошая функция, потому что она не позволяет мне вводить недопустимые аргументы при создании любого из объектов ниже.

Также я заметил, что если у меня есть строка super, прежде чем я установлю переменные, тогда проблема будет решена, так как объект самого высокого уровня перезапишет нижние уровни. Однако мой код в значительной степени построен на конце super, так как все фактические «установки» выполняются низкоуровневыми классами, тогда как классы более высокого уровня передают значение, которое устанавливается в цепочке наследования. Существуют ли инструкции о том, где вписаться в родительские вызовы?

Есть идеи Stackoverflow?

спасибо

p.s. Это просто плохой дизайн «кооперативных классов»? Что такое кооперативный класс? Есть ли правила или рекомендации, которым нужно следовать?

редактирование:

Stackoverflow не позволит мне ответить на мой собственный вопрос, но я нашел решение.

Я не думаю, что должны быть какие-либо общие члены между кооперативными классами. Если когда-либо существуют два класса, которые не зависят друг от друга (в том смысле, что нет отношения типа «это своего рода»), которые разделяют члена, то вам следует добавить уровень абстракции в отношение наследования.

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

Ниже приведен исправленный код. В соответствии со ссылкой в ​​OP я ввел базовый класс Root

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

class Root(object):
    pass

class A(Root):
    pass

class HasAZ(Root):
    def __init__(self, z=None, **kwargs):
        self.z = z
        super(HasAZ, self).__init__(**kwargs)

class B(HasAZ, A):
    pass


class C(HasAZ, A):
    pass

class D(B, C):
    pass

d = D(z='y')

for arg, value in d.__dict__.iteritems():
    print arg, ':', value

Ответы [ 2 ]

1 голос
/ 23 февраля 2012

Это просто плохой дизайн "кооперативных классов"?

Да, особенно из-за конфликта атрибутов.Если бы у вас не было атрибута с тем же именем, это не было бы проблемой, так как B.z не было бы перезаписано C.z.

Что такое кооперативный класс?? Есть ли правила или рекомендации, которым нужно следовать?

Тот, который вы уже прочитали:

http://rhettinger.wordpress.com/2011/05/26/super-considered-super/

Насколько я знаю, это лучший источникдля правил / руководящих принципов для кооперативных классов.

У вас есть выбор.Вы можете:

  • сделать имена своих атрибутов уникальными для всех классов в дереве наследования

или

  • всегда передавать все аргументы вниздерево.В этот момент вы также можете изменить все подписи на __init__(self, **kwargs)

или

  • не использовать множественное наследование
1 голос
/ 23 февраля 2012

Может быть, есть лучший способ сделать это, но это должно избежать перезаписи ::

class C(A):
    def __init__(self, z=None, **kwargs):
        if not hasattr(self, 'z'):
            self.z = z
        super(C, self).__init__(**kwargs)
...