python - 按程序创建一个 python 函数(特别是参数)

标签 python

问题

如何在 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/

相关文章:

python - 有没有办法为包含自身的类定义递归 __repr__ ?

javascript - 处理 javascript 的最简单的网络抓取工具是什么

python - 为什么 Tensorflow 转置在特定场景下失败?

python - 对象的描述符 '__dict__' 不适用于使用 type() 的对象

python - 在Python中分割多字标签的有效方法

python - Dataframe:在同一列中具有非 NaN 的先前值的掩码

python - 使用列中的值对 pandas 数据框进行多索引

python - TensorFlow 对象检测 API 奇怪的行为

python——将电子邮件附件抓取到磁盘?

Python 列表说明 : Assigning values directly is not possible