Подтвердить что ты не робот

Как эффективно передавать аргументы (** kwargs в python)

У меня есть класс, который наследуется от 2 других классов. Это базовые классы:

class FirstBase(object):
      def __init__(self, detail_text=desc, backed_object=backed_object,
                   window=window, droppable_zone_obj=droppable_zone_obj,
                   bound_zone_obj=bound_zone_object,
                   on_drag_opacity=on_drag_opacity):
          # bla bla bla

class SecondBase(object):
      def __init__(self, size, texture, desc, backed_object, window):
          # bla bla bla

И это ребенок:

class Child(FirstBase, SecondBase):
       """ this contructor doesnt work
       def __init__(self, **kwargs):
          # PROBLEM HERE
          #super(Child, self).__init__(**kwargs)
       """
       #have to do it this TERRIBLE WAY
       def __init__(self, size=(0,0), texture=None, desc="", backed_object=None,
                    window=None, droppable_zone_obj=[], bound_zone_object=[],
                    on_drag_opacity=1.0):
          FirstBase.__init__(self, detail_text=desc, backed_object=backed_object,
                             window=window, droppable_zone_obj=droppable_zone_obj,
                             bound_zone_obj=bound_zone_object,
                             on_drag_opacity=on_drag_opacity)
          SecondBase.__init__(self, size, texture, desc, backed_object, window)

Я хотел все это хорошо решить с помощью **kwargs, но когда я вызываю первый конструктор с комментариями, я получаю TypeError: __init__() got an unexpected keyword argument 'size'.

Любые идеи, как я могу заставить его работать с **kwargs?

4b9b3361

Ответ 1

Ваша проблема в том, что вы только пытались использовать super в дочернем классе.

Если вы используете super в базовых классах, это будет работать. Каждый конструктор "съест" аргументы ключевого слова, которые он принимает, и не передает их следующему конструктору. Когда вызывается конструктор для object, если есть какие-либо аргументы ключевого слова, оставшиеся после него, возникает исключение.

class FirstBase(object):
    def __init__(self, detail_text=None, backed_object=None,
                 window=None, droppable_zone_obj=None,
                 bound_zone_obj=None, on_drag_opacity=None, **kwargs):
        super(FirstBase, self).__init__(**kwargs)

class SecondBase(object):
    def __init__(self, size=(0,0), texture=None, desc="",
                 backed_object=None, window=None, **kwargs):
        super(SecondBase, self).__init__(**kwargs)

class Child(FirstBase, SecondBase):
    def __init__(self, **kwargs):
        super(Child, self).__init__(**kwargs)

Как вы можете видеть, он работает, за исключением случаев, когда вы передаете ложный аргумент ключевого слова:

>>> Child()
<__main__.Child object at 0x7f4aef413bd0>
>>> Child(detail_text="abc")
<__main__.Child object at 0x7f4aef413cd0>
>>> Child(bogus_kw=123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 14, in __init__
    super(Child, self).__init__(**kwargs)
  File "test.py", line 5, in __init__
    super(FirstBase, self).__init__(**kwargs)
  File "test.py", line 10, in __init__
    super(SecondBase, self).__init__(**kwargs)
TypeError: object.__init__() takes no parameters

Ответ 2

Мне не нравится это решение, но вы можете это сделать:

class FirstBase(object):
  def __init__(self, *args, **kargs):
      kargs.get('karg name', 'default value')
      # bla bla bla

class SecondBase(object):
  def __init__(self, *args, **kargs):
      kargs.get('karg name', 'default value')
      # bla bla bla

class Child(FirstBase, SecondBase):
   def __init__(self, *args, **kargs):
      FirstBase.__init__(self, *args, **kargs)
      SecondBase.__init__(self, *args, **kargs)

Ответ 3

Потому что FirstBase.__init__ не имеет аргумента size. Вы можете либо добавить аргумент size, либо добавить **kwargs в FirstBase.__init__.

Ответ 4

Попробуйте что-то вроде этого.

class FirstBase(object):
      def __init__(self, detail_text, backed_object, window, droppable_zone_obj, bound_zone_obj, on_drag_opacity):
          # bla bla bla

class SecondBase(object):
      def __init__(self, texture, desc, backed_object, window, size=None):
          # bla bla bla

Ответ 5

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

import inspect
#define a test function with two parameters function
def foo(a,b):
    return a+b

#obtain the list of the named arguments
acceptable = inspect.getargspec(f).args

print acceptable
#['a', 'b']

#test dictionary of kwargs
kwargs=dict(a=3,b=4,c=5)

#keep only the arguments that are both in the signature and in the dictionary
new_kwargs = {k:kwargs[k] for k in acceptable if k in kwargs}

#launch with the sanitized dictionary
f(**new_kwargs)

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

Ответ 6

Поскольку у вас есть контроль над кодом, простой способ позволить себе просто пройти ** kwargs так, как вы хотите, это добавить параметр ** kwargs в __init__ обоих базовых классов:

class FirstBase(object):
  def __init__(self, detail_text=desc, backed_object=backed_object,
               window=window, droppable_zone_obj=droppable_zone_obj,
               bound_zone_obj=bound_zone_object,
               on_drag_opacity=on_drag_opacity, **kwargs):
      # bla bla bla

class SecondBase(object):
      def __init__(self, size, texture, desc, backed_object, window, **kwargs):
          # bla bla bla

Сбой, с которым вы столкнулись, состоит в том, что у ваших базовых классов были только именованные параметры, поэтому, когда вы передаете неизвестное имя, вышибала говорит, что вы не в моем списке. Добавляя ** kwargs параметры к __init__-функциям базовых классов, они внезапно позволяют передавать любое произвольное именованное значение, что понятно, поскольку, в конце концов, ** kwargs - это открытый доступ ко всем, ничто из которого не является так, как бы он знал, что исключить? Он просто заканчивается словарем названных значений в глазах вызываемого кода, который их использует или нет. Вы можете полностью игнорировать ** kwargs.

Ответ, который на момент написания в настоящее время считается правильным, Дитрих, путал существенный вопрос - он вообще не имеет ничего общего с супер(), и его исправление работает просто потому, что ** kwargs были добавлено и открыло дверь для любого произвольного имени.

Если у вас нет контроля над кодом, вы можете:

  • передать аргументы явно, чтобы не распаковывать * args слишком долго или ** kwargs с неуказанными именами,
  • Посторонние параметры pop out из kwargs, используя функцию popup словаря,
  • или вы можете использовать эту вспомогательную мета-функцию:

    import inspect
    
    def anykwarg(function, *args, **kwargs):
        validargs = inspect.getargspec(function).args
        removals = [key for key in kwargs.keys() if key not in validargs]
        for removal in removals:
            del kwargs[removal]
        return function(*args, **kwargs)
    

Этот помощник anykwarg может быть улучшен для проверки параметра kwargs и, если он присутствует, не удаляет какие-либо значения из переданных kwargs, а проверенный выше .exetterspec возвращает эту информацию. Обратите внимание, что это нормально для kwargs в anykwarg, потому что это не ссылка на какой-либо словарь, который передается, это локальный словарь, созданный из параметров.

Ответ 7

Вам не нужно вызывать super() в базовых классах, как в принятом ответе @Dietrich Epp. Все, что вы делаете, это передать оставшиеся аргументы в kwargs до базового класса object, где они отбрасываются; это неаккуратно.

Когда вы выполняете super(ChildClass, self).__init__(foo, bar, **kwargs), суперкласс (es) ChildClass должен ожидать все аргументы foo, bar и **kwargs. Поэтому в вашем случае получение ошибки TypeError: __init__() got an unexpected keyword argument 'size' означает, что ваши родительские классы Child либо не имеют size, либо не имеют **kwargs в своих методах __init__().

Когда ваш вызов super() переходит к каждому из Firstbase и Secondbase, их конструкторы попытаются распаковать **kwargs в свои аргументы ключевых слов. Все, что находится в **kwargs, должно куда-то идти. Любое оставшееся (т.е. не указанное аргументом ключевого слова), например size, поднимет ваш TypeError, если для остатков нет другого **kwargs входите.