[英]how to pass arguments efficiently (**kwargs in python)
I have a class that inherits from 2 other classes. 我有一个继承自其他2个类的类。 These are the base classes:
这些是基类:
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
And this is the child: 这就是孩子:
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)
I wanted to solve it all nicely with **kwargs
but when I call the first commented out constructor I get TypeError: __init__() got an unexpected keyword argument 'size'
. 我想用
**kwargs
很好地解决它,但当我调用第一个注释掉的构造函数时,我得到TypeError: __init__() got an unexpected keyword argument 'size'
。
Any ideas how I can make it work with **kwargs?
我有什么想法可以让它与
**kwargs?
Your problem is that you only tried to use super
in the child class. 您的问题是您只尝试在子类中使用
super
。
If you use super
in the base classes too, then this will work. 如果你也在基类中使用
super
,那么这将有效。 Each constructor will "eat" the keyword arguments it takes and not pass them up to the next constructor. 每个构造函数将“吃掉”它所采用的关键字参数,而不是将它们传递给下一个构造函数。 When the constructor for
object
is called, if there are any keyword arguments left over it will raise an exception. 当调用
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)
As you can see, it works, except when you pass a bogus keyword argument: 如您所见,它可以工作,除非您传递一个伪造的关键字参数:
>>> 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
I don't like this solution but you can do this: 我不喜欢这个解决方案,但你可以这样做:
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)
Because FirstBase.__init__
doesn't have the size
argument. 因为
FirstBase.__init__
没有size
参数。 You can either add the size
argument or add **kwargs
to FirstBase.__init__
. 您可以添加
size
参数或将**kwargs
添加到FirstBase.__init__
。
Try something like this. 尝试这样的事情。
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
a possible hack (but hacking is bad, remember), is to appeal to the inspect module to obtain the signature of the function that you are going to use: 一个可能的黑客(但黑客是坏的,记住),是要求检查模块获取你将要使用的功能的签名:
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)
This is not a code you should use in production code. 这不是您应该在生产代码中使用的代码。 If the function refuses some arguments it means that you can obtain the signature reading the code, and passing only the necessary keys.
如果函数拒绝某些参数,则意味着您可以获取读取代码的签名,并仅传递必要的键。 The hack with inspect is far less stable and readable, so use only when you haven't any other way!
检查的黑客程度远不那么稳定和可读,所以只有在没有其他任何方式时才使用!
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: 由于你可以控制代码,所以让你自己以你想要的方式传递** 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
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?
通过将** kwargs参数添加到基类的__init__函数中,它们突然允许任意命名值被简单地传入,这是可以理解的,因为毕竟** kwargs是一个开放式的catch all,其中没有任何东西是命名,那怎么知道排除什么? 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.
你可以完全忽略** 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. 在写作的那一刻,目前被Dietrich认为是正确的答案,这个答案混淆了基本问题 - 它与super()完全没有关系,而且他的修正只是因为** kwargs被添加而且为任意名字打开了大门。
If you don't have control over the code, you can: 如果您无法控制代码,则可以:
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. 可以改进这个anykwarg帮助器以检查kwargs参数,如果存在则不从传入的kwargs中删除任何值,并且上面使用的inspect.getargspec返回该信息。 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.
注意,在anykwarg中可以从kwargs中删除它,因为它不是对传入的某些字典的引用,它是从参数生成的本地字典。
You should not have to call super()
in the base classes, as in the accepted answer by @Dietrich Epp . 你不应该在基类中调用
super()
,就像@Dietrich Epp接受的答案一样 。 All you're doing is passing any leftover arguments in kwargs
up to the object
base class, where they're thrown away; 你所做的只是将任何剩余的参数传递给
kwargs
到object
基类,然后扔掉它们; this is sloppy. 这很草率。
When you do super(ChildClass, self).__init__(foo, bar, **kwargs)
, the super class(es) of ChildClass
must expect all the arguments foo
, bar
, and **kwargs
. 当你做
super(ChildClass, self).__init__(foo, bar, **kwargs)
, ChildClass
的超类必须要求所有参数foo
, bar
和**kwargs
。 So in your case, getting the error TypeError: __init__() got an unexpected keyword argument 'size'
implies your parent classes of Child
either don't have size
or don't have **kwargs
in their __init__()
methods. 因此,在您的情况下,获取错误
TypeError: __init__() got an unexpected keyword argument 'size'
意味着您的父类Child
没有size
或者__init__()
方法中没有**kwargs
。
When your super()
call goes to each of Firstbase
and Secondbase
, their constructors will try and unpack **kwargs
into their keyword arguments. 当你的
super()
调用转到Firstbase
和Secondbase
每一个时,它们的构造函数将尝试将**kwargs
解包为其关键字参数。 Everything in **kwargs
needs to go somewhere. **kwargs
所有东西都需要去某个地方。 Anything leftover (ie not specified by a keyword argument), like size
, will raise your TypeError, unless there is another **kwargs
for the leftovers to go into. 剩下的任何东西(即没有由关键字参数指定),如
size
,都会引发你的TypeError,除非剩下剩下的**kwargs
进入。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.