[英]how to pass arguments efficiently (**kwargs in 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?
您的问题是您只尝试在子类中使用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
我不喜欢这个解决方案,但你可以这样做:
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)
因为FirstBase.__init__
没有size
参数。 您可以添加size
参数或将**kwargs
添加到FirstBase.__init__
。
尝试这样的事情。
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
一个可能的黑客(但黑客是坏的,记住),是要求检查模块获取你将要使用的功能的签名:
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)
这不是您应该在生产代码中使用的代码。 如果函数拒绝某些参数,则意味着您可以获取读取代码的签名,并仅传递必要的键。 检查的黑客程度远不那么稳定和可读,所以只有在没有其他任何方式时才使用!
由于你可以控制代码,所以让你自己以你想要的方式传递** 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。
在写作的那一刻,目前被Dietrich认为是正确的答案,这个答案混淆了基本问题 - 它与super()完全没有关系,而且他的修正只是因为** 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中删除它,因为它不是对传入的某些字典的引用,它是从参数生成的本地字典。
你不应该在基类中调用super()
,就像@Dietrich Epp接受的答案一样 。 你所做的只是将任何剩余的参数传递给kwargs
到object
基类,然后扔掉它们; 这很草率。
当你做super(ChildClass, self).__init__(foo, bar, **kwargs)
, ChildClass
的超类必须要求所有参数foo
, bar
和**kwargs
。 因此,在您的情况下,获取错误TypeError: __init__() got an unexpected keyword argument 'size'
意味着您的父类Child
没有size
或者__init__()
方法中没有**kwargs
。
当你的super()
调用转到Firstbase
和Secondbase
每一个时,它们的构造函数将尝试将**kwargs
解包为其关键字参数。 **kwargs
所有东西都需要去某个地方。 剩下的任何东西(即没有由关键字参数指定),如size
,都会引发你的TypeError,除非剩下剩下的**kwargs
进入。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.