Python - 由变量定义的精确参数数量

Lan*_*ton 4 python lambda anonymous-function variadic-functions

我正在创建一个方法来构造一个匿名方法来返回多个变量的函数,例如f(x,y,z)= b.我希望用户能够传递变量列表:

def get_multivar_lambda(expression, variables=["x"])
Run Code Online (Sandbox Code Playgroud)

然后我希望返回的匿名函数采用精确的len(variables)参数(基于列表索引的位置或基于列表中字符串的关键字).我知道我可以使用*args和检查长度,但这似乎不优雅.

这可能吗?我怎么能这样做?

这是我如何为一个变量(其中seval是一个来自模块simple_eval)的例子:

def get_lambda(expression, variable="x"):                                       
    return lambda arg: seval(expression.replace(variable, str(arg))) 
Run Code Online (Sandbox Code Playgroud)

这是我如何通过检查arguments*传递的长度来做到的:

def get_multivar_lambda(expression, variables=["x"]):

    def to_return(*arguments):
        if len(variables) != len(arguments):
            raise Exception("Number of arguments != number of variables")
        for v, a in zip(variables, arguments):
            expression.replace(v, a)
        return seval(expression)

    return to_return
Run Code Online (Sandbox Code Playgroud)

编辑:我从用户输入中获取表达式和变量,因此一种安全的方法是最好的.

Ash*_*ary 6

如果你可以使用Python 3,那么新引入的(Python的3.3+)inspect.Signatureinspect.Parameter可以使你的代码非常干净(PEP 362 -函数签名的对象).这些在装饰器中也非常方便:

from inspect import Parameter, signature, Signature

def get_multivar_lambda(expression, variables=["x"]):

    params = [Parameter(v, Parameter.POSITIONAL_OR_KEYWORD) for v in variables]
    sig = Signature(params)

    def to_return(*args, **kwargs):
        values = sig.bind(*args, **kwargs)
        for name, val in values.arguments.items():
            print (name, val)

    to_return.__signature__ = signature(to_return).replace(parameters=params)
    return to_return
Run Code Online (Sandbox Code Playgroud)

演示:

>>> f = get_multivar_lambda('foo')
>>> f(1)
x 1
>>> f(1, 2)
Traceback (most recent call last):
  File "<pyshell#43>", line 1, in <module>
  ...
    raise TypeError('too many positional arguments') from None
TypeError: too many positional arguments
>>> f(x=100)
x 100
Run Code Online (Sandbox Code Playgroud)

将为用户生成有用的错误消息:

>>> g = get_multivar_lambda('foo', variables=['x', 'y', 'z'])
>>> g(20, 30, x=1000)
Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    ....
TypeError: multiple values for argument 'x'
>>> g(1000, y=2000, z=500)
x 1000
y 2000
z 500
Run Code Online (Sandbox Code Playgroud)

用于内省目的的功能签名:

>>> inspect.getargspec(g)
ArgSpec(args=['x', 'y', 'z'], varargs=None, keywords=None, defaults=None)
Run Code Online (Sandbox Code Playgroud)