13

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

4

7 回答 7

19

您的问题是您仅尝试super在子类中使用。

如果您super也在基类中使用,那么这将起作用。每个构造函数都会“吃掉”它所接受的关键字参数,而不是将它们传递给下一个构造函数。当object调用 for 的构造函数时,如果有剩余的关键字参数,它将引发异常。

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
于 2012-10-29T15:59:31.317 回答
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)
于 2012-10-29T16:10:27.697 回答
1

因为FirstBase.__init__没有size论据。您可以添加size参数或添加**kwargsFirstBase.__init__.

于 2012-10-29T15:50:21.913 回答
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
于 2012-10-29T15:57:17.287 回答
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)

这不是您应该在生产代码中使用的代码。如果函数拒绝某些参数,则意味着您可以获得读取代码的签名,并且只传递必要的密钥。带有inspect 的hack 的稳定性和可读性要差得多,所以只有在没有其他方法时才使用!

于 2012-10-30T00:37:53.453 回答
0

Since you have control over the code, the easy way to let yourself just pass around **kwargs the way you want is to add a **kwargs parameter to the __init__ of both base classes, as so:

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

The failure you had encountered is because your base classes only had named parameters, therefore when you pass an unknown name, the bouncer says you're not on my list. By adding **kwargs parameters to the __init__ functions of the base classes, they suddenly allow any arbitrary named value to simply be passed in, which is understandable since, after all, **kwargs is an open ended catch all, nothing of which is named, so how would it know what to exclude? It simply ends up a dictionary of named values in the eyes of the called code, which uses them or doesn't. You could completely ignore **kwargs.

The answer which at the moment of writing is currently accepted as the right one, by Dietrich, confused the essential issue -- it has nothing to do with super() at all, and his correction works simply because **kwargs were added and that opened the door for any arbitrary name.

If you don't have control over the code, you can:

  • pass arguments explicitly so as not to unpack *args too long, or **kwargs with unspecified names,
  • pop extraneous parameters out of kwargs using the dictionary pop function,
  • or you could use this helper meta-function:

    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)
    

This anykwarg helper could be improved to check for a kwargs parameter and if present not remove any values from the kwargs passed in, and inspect.getargspec used above returns that information. Note, it is okay to del from kwargs in anykwarg because it is not a reference to some dictionary passed in, it is a local dictionary generated from parameters.

于 2015-08-21T07:55:48.370 回答
-1

您不必像@Dietrich Epp 接受的答案super()那样调用基类。您所做的就是将任何剩余的参数传递给基类,然后将它们丢弃;这是马虎。kwargsobject

当您这样做时super(ChildClass, self).__init__(foo, bar, **kwargs), 的超类ChildClass必须期待所有参数foobar**kwargs。因此,在您的情况下,收到错误TypeError: __init__() got an unexpected keyword argument 'size'意味着您的父类在其方法中Child没有size或没有。**kwargs__init__()

当您super()调用每个FirstbaseandSecondbase时,它们的构造函数将尝试解包**kwargs到它们的关键字参数中。一切都**kwargs需要去某个地方。任何剩余的东西(即不是由关键字参数指定的),例如size,都会引发您的 TypeError,除非剩余的东西还有另一个**kwargs可以进入。

于 2015-11-09T22:09:59.020 回答