Выборочное наследование в дочернем классе в Python - PullRequest
0 голосов
/ 07 мая 2019

У меня определены следующие классы:

class First(object):
    def __init__(self):
        print("first")

    def mF1(self):
        print "first 1"

    def mF2(self):
        print "first 2"


class Second(object):
    def __init__(self):
        print("second")

    def mS1(self):
        print "second 1"


class Third(object):
    def __init__(self):
        print("third")

    def mT1(self):
        print "third 1"

    def mT2(self):
        print "third 2"

    def mT3(self):
        print "third 3"


class Fourth(First, Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print("fourth")


C = Fourth()
C.mF1()
C.mF2()
C.mS1()
C.mT1()
C.mT2()
C.mT3()

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

first
fourth
first 1
first 2
second 1
third 1
third 2
third 3

При этом, довольно ясно, что все атрибуты и методы классов First, Second, Third и Fourth доступны в классе Fourth.Теперь я хотел бы, чтобы класс Fourth выборочно наследовал от родителей на основе контекста - то есть от одного First или от First и Third и т. Д. Один из способов - определить отдельные классы следующим образом:

class Fourth1(First):
    def __init__(self):
        super(Fourth, self).__init__()
        print("fourth first")


class Fourth2(First, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print("fourth first third")

что означает определение отдельных классов и использование отдельных имен классов вместо одного.

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

C = Fourth(1,0,0)

, чтобы наследовать от First и

C = Fourth(1,0,1)

для наследования от First и Third?

1 Ответ

1 голос
/ 07 мая 2019

Это можно сделать через __new__. Поскольку можно динамически создавать классы, наследуемые от других классов, а __new__ может создавать объекты произвольных типов:

class Fourth(object):
    """BEWARE special class that creates objects of subclasses"""
    classes = [None] * 8

    def __new__(cls, first, second, third):
        index = 1 if first else 0
        index += 2 if second else 0
        index += 4 if third else 0
        if not cls.classes[index]:
            parents = [Fourth]
            if first: parents.append(First)
            if second: parents.append(Second)
            if third: parents.append(Third)
            ns = {'__new__': object.__new__,
                  '__init__': Fourth._child__init__,
                  '__doc__': Fourth.__doc__}
            cls.classes[index] = type('Fourth', tuple(parents), ns)
        return object.__new__(cls.classes[index])

    def _child__init__(self, first = None, second=None, third=None):
        Fourth.__init__(self)

    def __init__(self):
        print("Fourth")

После этого вы можете делать то, что хотели:

>>> c = Fourth(1,0,0)
Fourth
>>> c2 = Fourth(1,1,1)
Fourth
>>> c
<__main__.Fourth1 object at 0x0000024026151780>
>>> c2
<__main__.Fourth7 object at 0x000002402616E8D0>
>>> c2.mT1()
third 1
>>> c.mT1()
Traceback (most recent call last):
  File "<pyshell#302>", line 1, in <module>
    c.mT1()
AttributeError: 'Fourth1' object has no attribute 'mT1'

Но я настоятельно советую вам не взламывать, если у вас нет для этого веских причин. Потому что это заканчивается наличием класса (Fourth), который создает объекты не из себя, а из подклассов. И объект, очевидно принадлежащий этому классу, будет иметь другое поведение. Это будет беспокоить будущих читателей и сопровождающих

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