开发者社区> 问答> 正文

*args和**kwargs的强制参数签名

你有一个函数或方法,它使用*args和**kwargs作为参数,这样使得它比较通用, 但有时候你想检查传递进来的参数是不是某个你想要的类型。

展开
收起
哦哦喔 2020-04-17 16:19:22 1172 0
2 条回答
写回答
取消 提交回答
  • 有点尴尬唉 你要寻找的东西已经被吃掉啦!

    在我们需要构建通用函数库、编写装饰器或实现代理的时候,对于 *args 和 **kwargs 的使用是很普遍的。 但是,这样的函数有一个缺点就是当你想要实现自己的参数检验时,代码就会笨拙混乱。

    2020-04-17 17:38:39
    赞同 展开评论 打赏
  • 对任何涉及到操作函数调用签名的问题,你都应该使用 inspect 模块中的签名特性。 我们最主要关注两个类:Signature 和 Parameter 。下面是一个创建函数前面的交互例子:
    
    >>> from inspect import Signature, Parameter
    >>> # Make a signature for a func(x, y=42, *, z=None)
    >>> parms = [ Parameter('x', Parameter.POSITIONAL_OR_KEYWORD),
    ...         Parameter('y', Parameter.POSITIONAL_OR_KEYWORD, default=42),
    ...         Parameter('z', Parameter.KEYWORD_ONLY, default=None) ]
    >>> sig = Signature(parms)
    >>> print(sig)
    (x, y=42, *, z=None)
    >>>
    一旦你有了一个签名对象,你就可以使用它的 bind() 方法很容易的将它绑定到 *args 和 **kwargs 上去。 下面是一个简单的演示:
    
    >>> def func(*args, **kwargs):
    ...     bound_values = sig.bind(*args, **kwargs)
    ...     for name, value in bound_values.arguments.items():
    ...         print(name,value)
    ...
    >>> # Try various examples
    >>> func(1, 2, z=3)
    x 1
    y 2
    z 3
    >>> func(1)
    x 1
    >>> func(1, z=3)
    x 1
    z 3
    >>> func(y=2, x=1)
    x 1
    y 2
    >>> func(1, 2, 3, 4)
    Traceback (most recent call last):
    ...
        File "/usr/local/lib/python3.3/inspect.py", line 1972, in _bind
            raise TypeError('too many positional arguments')
    TypeError: too many positional arguments
    >>> func(y=2)
    Traceback (most recent call last):
    ...
        File "/usr/local/lib/python3.3/inspect.py", line 1961, in _bind
            raise TypeError(msg) from None
    TypeError: 'x' parameter lacking default value
    >>> func(1, y=2, x=3)
    Traceback (most recent call last):
    ...
        File "/usr/local/lib/python3.3/inspect.py", line 1985, in _bind
            '{arg!r}'.format(arg=param.name))
    TypeError: multiple values for argument 'x'
    >>>
    可以看出来,通过将签名和传递的参数绑定起来,可以强制函数调用遵循特定的规则,比如必填、默认、重复等等。
    
    下面是一个强制函数签名更具体的例子。在代码中,我们在基类中先定义了一个非常通用的 __init__() 方法, 然后我们强制所有的子类必须提供一个特定的参数签名。
    
    from inspect import Signature, Parameter
    
    def make_sig(*names):
        parms = [Parameter(name, Parameter.POSITIONAL_OR_KEYWORD)
                for name in names]
        return Signature(parms)
    
    class Structure:
        __signature__ = make_sig()
        def __init__(self, *args, **kwargs):
            bound_values = self.__signature__.bind(*args, **kwargs)
            for name, value in bound_values.arguments.items():
                setattr(self, name, value)
    
    # Example use
    class Stock(Structure):
        __signature__ = make_sig('name', 'shares', 'price')
    
    class Point(Structure):
        __signature__ = make_sig('x', 'y')
    下面是使用这个 Stock 类的示例:
    
    >>> import inspect
    >>> print(inspect.signature(Stock))
    (name, shares, price)
    >>> s1 = Stock('ACME', 100, 490.1)
    >>> s2 = Stock('ACME', 100)
    Traceback (most recent call last):
    ...
    TypeError: 'price' parameter lacking default value
    >>> s3 = Stock('ACME', 100, 490.1, shares=50)
    Traceback (most recent call last):
    ...
    TypeError: multiple values for argument 'shares'
    >>>
    
    2020-04-17 16:19:31
    赞同 展开评论 打赏
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载