问题
如何在 Python 中按程序创建一个函数,该函数采用特定的命名参数,但允许这些参数名称由数据驱动?
示例
假设您想要创建一个类装饰器 with_init
,它添加一个带有特定命名参数的 __init__
方法,以便以下两个类是等效的。
class C1(object):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
@with_init('x y z')
class C2(object):
pass
我的第一次尝试通过创建一个接受 *args
而不是特定命名参数的函数来作弊:
class with_init(object):
def __init__(self, params):
self.params = params.split()
def __call__(self, cls):
def init(cls_self, *args):
for param, value in zip(self.params, args):
setattr(cls_self, param, value)
cls.__init__ = init
return cls
它在某些情况下有效:
>>> C1(1,2,3)
<__main__.C1 object at 0x100c410>
>>> C2(1,2,3)
<__main__.C2 object at 0x100ca70>
但在其他方面则不然:
>>> C2(1,2,3,4) # Should fail, but doesn't.
<__main__.C2 object at 0x100cc90>
>>> C2(x=1, y=2, z=3) # Should succeed, but doesn't.
Traceback (most recent call last):
File "<string>", line 1, in <fragment>
TypeError: init() got an unexpected keyword argument 'y'
当然,我可以向嵌套的 init
函数添加代码来尝试检查每种可能的情况,但似乎应该有一种更简单的方法。
我注意到collections.namedtuple
通过将字符串传递给 exec 来避免这些问题。这对我来说似乎很迂回,但也许这就是解决方案。
with_init.__call__
的正确实现是什么?
注意:我想要一个 Python 2.x 解决方案。
最佳答案
非常粗略。这接受 kw args 并检查参数的数量是否正确
def __call__(self, cls):
def init(cls_self, *args, **kw):
if len(args)+len(kw) != len(self.params):
raise RuntimeError("Wrong number of arguments")
for param, value in zip(self.params, args):
setattr(cls_self, param, value)
vars(cls_self).update(kw)
cls.__init__ = init
return cls
此版本有一些改进
def __call__(self, cls):
def init(cls_self, *args, **kw):
for param, value in zip(self.params, args):
if param in kw:
raise TypeError("Multiple values for %s"%param)
kw[param]=value
if len(args) > len(self.params) or set(kw) != set(self.params):
raise TypeError("Wrong number of arguments")
vars(cls_self).update(kw)
cls.__init__ = init
return cls
此版本还告诉您有关意外关键字参数的信息
def __call__(self, cls):
def init(cls_self, *args, **kw):
for param, value in zip(self.params, args):
if param in kw:
raise TypeError("Multiple values for %s"%param)
kw[param]=value
unexpected_args = list(set(kw)-set(self.params))
if unexpected_args:
raise TypeError("Unexpected args %s"%unexpected_args)
missing_args = list(set(self.params)-set(kw))
if missing_args:
raise TypeError("Expected args %s"%missing_args)
vars(cls_self).update(kw)
cls.__init__ = init
return cls
关于python - 按程序创建一个 python 函数(特别是参数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2375316/