Передача аргументов **kwargs родительским классам при множественном наследовании приводит к потере содержимого kwargs

Как в сценарии множественного наследования Python3 передает аргументы всем родительским классам? Рассмотрим игрушку ниже:

class A(object):
    def __init__(self, a='x', **kwargs):
        print('A', kwargs)
        super().__init__()

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

class C(A,B):
    def __init__(self, **kwargs):
        print('C', kwargs)
        super().__init__(**kwargs)

c = C(a='A', b='B', c='C')

Результат:

C {'a': 'A', 'b': 'B', 'c': 'C'}
A {'b': 'B', 'c': 'C'}
B {}

Что я ожидаю сделать, так это передать одни и те же kwargs всем родительским классам и позволить им использовать значения по своему усмотрению. Но я вижу, что как только я опускаюсь до первого родительского класса A, kwargs потребляется и ничего не передается B.

Пожалуйста помоги!


Обновлять Если бы порядок наследования был детерминированным и я знал исчерпывающий список kwargs, которые могут быть переданы по цепочке наследования, мы могли бы решить эту проблему так:

class A(object):
    def __init__(self, a='x', **kwargs):
        print('A', kwargs)
        super().__init__(**kwargs)

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

class C(A,B):
    def __init__(self, **kwargs):
        print('C', kwargs)
        super().__init__(**kwargs)

c = C(a='A', b='B', c='C')

Здесь, поскольку A передает kwargs и мы знаем, что B будет вызываться после него, мы в безопасности, и к моменту вызова object.__init__() kwargs будут пусты.

Однако это может быть не всегда так.

Рассмотрим этот вариант:

class C(B,A):
    def __init__(self, **kwargs):
        print('C', kwargs)
        super().__init__(**kwargs)

Здесь object.__init__(), вызванный из class A, вызовет исключение, поскольку есть еще kwargs, которые можно использовать.

Итак, есть ли общее руководство по дизайну, которому я должен следовать?

Ну, в A вы использовали super().__init__(), но если бы вы хотели передать их, вы бы использовали super().__init__(a=a, **kwargs) вместо этого. Вам решать, передавать их (или нет), здесь нет никакой магии.

wim 11.01.2023 05:50

Проверьте, может ли помочь доступный здесь ответ: stackoverflow.com/questions/60495296/…

Hetal Thaker 11.01.2023 06:14

@wim В этом отношении ты прав. Если бы я передал его как super().__init__(**kwargs) в классе A, то класс B получил бы эти параметры. Однако это будет означать, что я знаю и могу гарантировать, что __init__ из object не получит никаких кваргов. Этого не будет, если я также создал экземпляр другого класса непосредственно из A как: d=A(a='x', b='b'). (Это будет утверждать). Я обновил вопрос, чтобы добавить больше контекста к проблеме.

Anshul 11.01.2023 07:15

Да, если вы передаете произвольные аргументы неизвестным родителям, ваш класс должен быть спроектирован как миксин, а не как необязательный автономный класс или миксин. Обычно в иерархии классов каждый класс определяет конкретные параметры и передает только остатки как kwargs или, по крайней мере, обеспечивает удаление используемых kwargs перед передачей остатков родительскому; тогда все кварги должны быть израсходованы, и object получит ноль кваргов, как и должно быть.

deceze 11.01.2023 07:20

@HetalThaker Ответ в упомянутой ссылке имеет смысл. Я также хотел бы понять общий шаблон проектирования. Все, что останется внутри kwargs к тому времени, когда оно достигнет конструктора object, вызовет проблемы, и мы не можем гарантировать (в моем коде), какой класс создает пользователь и какие данные он передает. Например, я не могу вставлять неподдерживаемые ключевые слова в class A, так как они могут использоваться последующими super. Может быть, у меня должен быть базовый класс, который обрабатывает этот случай и извлекает все, прежде чем вызывать его super(), и чтобы все остальные классы наследуются от него?

Anshul 11.01.2023 07:21

Вы разрабатываете свои классы для использования определенным образом. Если они используются неправильно, ничего страшного, если это приведет к ошибке. Вам просто нужно использовать свои классы так, как вы их разработали, или четко задокументировать, как они должны использоваться, если вы не собираетесь быть пользователем. Тогда не должно быть большого беспокойства по поводу того, что «возможно, другие классы используют аргумент» или «пользователь создает экземпляр неправильного класса».

deceze 11.01.2023 08:40

@Anshul stackoverflow.com/q/52272811/674039 может быть полезно/дублировать

wim 11.01.2023 20:31

@wim Да, ответ очень полезен для понимания общего процесса проектирования. То, что предложил Deceze, также в целом похоже. Итак, я резюмирую его ключевые моменты и закрываю этот вопрос. Большое спасибо!

Anshul 13.01.2023 05:02
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Привет, люди RPA, это снова я и я несу подарки! В очередном моем приключении о том, как создавать ботов для облегчения рутины. Вот, думаю, стоит...
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
Учебник по веб-скрапингу
Учебник по веб-скрапингу
Привет, ребята... В этот раз мы поговорим о веб-скрейпинге. Целью этого обсуждения будет узнать и понять, что такое веб-скрейпинг, а также узнать, как...
Тонкая настройка GPT-3 с помощью Anaconda
Тонкая настройка GPT-3 с помощью Anaconda
Зарегистрируйте аккаунт Open ai, а затем получите ключ API ниже.
1
8
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как было предложено @deceze, а также указано в обсуждении, которым поделился @wim в комментариях к вопросу, вот краткое изложение (дословно):

если вы передаете произвольные аргументы неизвестным родителям, ваш класс должен быть разработан как миксин, а не как необязательный автономный класс или миксин. Обычно в иерархии классов каждый класс определяет конкретные параметры и передает только остатки как kwargs или, по крайней мере, обеспечивает удаление используемых kwargs перед передачей остатков родительскому; тогда все kwargs должны быть израсходованы, и объект получит нулевые kwargs, как и должно быть.
Вы разрабатываете свои классы для использования определенным образом. Если они используются неправильно, ничего страшного, если это приведет к ошибке. Вам просто нужно использовать свои классы так, как вы их разработали, или четко задокументировать, как они должны использоваться, если вы не собираетесь быть пользователем. Тогда не должно быть большого беспокойства по поводу того, что «возможно, другие классы используют аргумент» или «пользователь создает экземпляр неправильного класса».

Резюме: Не слишком обобщайте классы и не пытайтесь сделать их слишком гибкими для сценария «на всякий случай», когда кто-то позже будет их использовать. В таких случаях должна быть надлежащая документация для лица, расширяющего классы.

Другие вопросы по теме