如何有效地传递参数(** python中的kwargs)

时间:2012-10-29 15:46:07

标签: python inheritance kwargs

我有一个继承自其他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?

一起使用

7 个答案:

答案 0 :(得分:19)

您的问题是您只是尝试在子类中使用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

答案 1 :(得分:3)

我不喜欢这个解决方案,但你可以这样做:

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)

答案 2 :(得分:1)

因为FirstBase.__init__没有size参数。您可以添加size参数,也可以将**kwargs添加到FirstBase.__init__

答案 3 :(得分:0)

尝试这样的事情。

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

答案 4 :(得分:0)

可能的黑客攻击(但是黑客行为很糟糕,请记住),是要求检查模块获取您将要使用的功能的签名:

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)

这不是您应该在生产代码中使用的代码。如果函数拒绝某些参数,则意味着您可以获取读取代码的签名,并仅传递必要的键。检查的黑客程度远不那么稳定和可读,所以只有在没有其他任何方式时才使用!

答案 5 :(得分:0)

由于你可以控制代码,所以让自己以你想要的方式传递** kwargs的简单方法是在两个基类的__init__中添加一个** kwargs参数,如下所示:

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是一个开放式的catch all,其中没有任何东西是命名,那怎么知道排除什么?它只是在被调用代码的眼睛中结束了一个命名值的字典,它使用或不使用它们。你可以完全忽略** kwargs。

目前正在接受写作的答案是迪特里希目前被接受为正确的答案,这使得基本问题变得困惑 - 它与super()毫无关系,而且他的纠正只是因为** kwargs是添加并为任意名称打开了大门。

如果您无法控制代码,可以:

  • 明确传递参数,以免解压* args太长时间,或**名称未指定的kwargs,
  • 使用字典pop函数
  • 从kwargs中弹出无关参数
  • 或者您可以使用此辅助元函数:

    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中删除任何值,并且上面使用的inspect.getargspec返回该信息。注意,在anykwarg中可以从kwargs中删除它,因为它不是对传入的某些字典的引用,它是从参数生成的本地字典。

答案 6 :(得分:-1)

您不必在基类中调用super(),就像在the accepted answer by @Dietrich Epp中一样。你所做的就是将kwargs中任何剩余的参数传递到object基类,然后扔掉它们;这很草率。

执行super(ChildClass, self).__init__(foo, bar, **kwargs)时,ChildClass的超类必须指望所有参数foobar和{ {1}}。因此,在您的情况下,收到错误**kwargs意味着您的TypeError: __init__() got an unexpected keyword argument 'size'父类没有Childsize中没有**kwargs方法

__init__()调用super()Firstbase时,他们的构造函数会尝试将Secondbase解压缩到关键字参数中。 **kwargs中的所有内容都需要去某处。任何剩余的内容(即未由关键字参数指定),如**kwargs,都会引发TypeError,除非有另一个{{1}剩下的东西进入。