Python структура наследования и аргументы - PullRequest
1 голос
/ 31 января 2020

Я пытаюсь разработать структуру класса, которая позволяет пользователю определять свой собственный класс, который перегружает предопределенные методы в других классах. В этом случае пользователь создаст класс C для перегрузки метода "function" в D. Созданный пользователем класс C имеет общие логики c для других пользовательских классов A и B, поэтому они наследуются от C перегружать "функцию", но также наследовать от D, чтобы использовать другие методы D. Вопрос, который у меня возникает, заключается в том, как передать «значение» из A и B в D и игнорировать передачу его в C. То, что я сейчас написал, приведет к ошибке, так как C не имеет «значения» в качестве аргумента.

Я знаю, что могу добавить «значение» (или * аргументы) к инициализации C метод и супер вызов, но я не хочу знать, какие входные данные нужны другим классам для добавления новых классов в A и B. Кроме того, если я поменяю местами порядок C и DI не получит ошибку но тогда я не использую перегруженную "функцию" C. Есть ли очевидный способ обойти это?

class D(SomethingElse):
    def __init__(self, value, **kwargs):
        super(D, self).__init__(**kwargs)

        self.value = value

    def function(self):
        return self.value

    def other_method(self):
        pass

class C(object):
    def __init__(self):
        super(C, self).__init__()

    def function(self):
        return self.value*2

class B(C, D):
    def __init__(self, value, **kwargs):
        super(B, self).__init__(value, **kwargs)

class A(C, D):
    def __init__(self, value, **kwargs):
        super(A, self).__init__(value, **kwargs)



a = A(3)
print(a.function())
>>> 6

Ответы [ 2 ]

0 голосов
/ 31 января 2020

Итак, я пытаюсь понять суть A и B. Я предполагаю, что, возможно, вы хотите смешать поведение суперкласса и иногда иметь локальное поведение. Итак, предположим, что A просто смешивает поведение, а B имеет локальное поведение и состояние.

Если вам не нужно ваше собственное состояние, вам, вероятно, не нужен __init__. Поэтому для A и C просто опустите __init__.

class SomethingElse(object):
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

class D(SomethingElse):
    def __init__(self, value, *args,  **kwargs):
        super(D, self).__init__(*args, **kwargs)

        self.value = value

    def function(self):
        return self.value

    def other_method(self):
        return self.__dict__

class C(object):
    #def __init__(self):
    #    super(C, self).__init__()

    def function(self):
        return self.value*2

class B(C, D):
    def __init__(self, value, bstate, *args, **kwargs):
        super(B, self).__init__(value, *args, **kwargs)
        self.bstate = bstate

    def __repr__(self):
        return (self.__class__.__name__ + ' ' +
                self.bstate + ' ' + str(self.other_method()))

class A(C, D):
    pass


a = A(3)
b = B(21, 'extra')

a.function()
6

b.function()
42
repr(a)
'<xx.A object at 0x107cf5e10>'
repr(b)
"B extra {'args': (), 'bstate': 'extra', 'value': 21, 'kwargs': {}}"

Я сохранил синтаксис python2, предполагая, что вы все еще можете его использовать, но, как указывает другой ответ, python3 упрощает синтаксис super(), и вы действительно должны использовать python3 сейчас.

Если вы поменяете местами C и D, вы измените порядок разрешения метода python, и это действительно изменит метод, в который разрешается вызов функции A.function.

0 голосов
/ 31 января 2020

По сути, есть две вещи, которые вам нужно сделать, чтобы ваши __init__ методы хорошо воспроизводились с множественным наследованием в Python:

  1. Всегда принимать параметр **kwargs и всегда вызывать super().__init__(**kwargs), , даже если вы думаете, что вы базовый класс . То, что ваш суперкласс object, не означает, что вы последний (до object) в порядке разрешения метода .
  2. Не передавайте аргументы __init__ родительского класса явно; передать их только через **kwargs. Ваш родительский класс не обязательно следует за вами в порядке разрешения методов, поэтому позиционные аргументы могут быть переданы неверному другому __init__ методу.

Это называется "кооперативным субклассированием". Давайте попробуем с вашим примером кода:

class D:
    def __init__(self, value, **kwargs):
        self.value = value
        super().__init__(**kwargs)

    def function(self):
        return self.value

class C:
    # add **kwargs parameter
    def __init__(self, **kwargs):
        # pass kwargs to super().__init__
        super().__init__(**kwargs)

    def function(self):
        return self.value * 2

class B(C, D):
    # don't take parent class's value arg explicitly
    def __init__(self, **kwargs):
        # pass value arg via kwargs
        super().__init__(**kwargs)

class A(C, D):
    # don't take parent class's value arg explicitly
    def __init__(self, **kwargs):
        # pass value arg via kwargs
        super().__init__(**kwargs)

Демо:

>>> a = A(value=3)
>>> a.value
3
>>> a.function()
6

Обратите внимание, что value должен быть передан конструктору A как аргумент ключевого слова, а не как позиционный аргумент. Также рекомендуется установить self.value = value перед вызовом super().__init__.

Я также упростил class C(object): до class C: и super(C, self) до super(), поскольку они эквивалентны Python 3.

...