I am creating a method that constructs an anonymous method to return a function of multiple variables e.g. f(x, y, z) = b. I want the user to be able to pass a list of variables:
def get_multivar_lambda(expression, variables=["x"])
I then want the returned anonymous function to take exactly len(variables)
arguments (either positional based on their list index or keyword based on the string in the list). I know I can use *args
and check the length, but this seems inelegant.
Is this possible? How might I do this?
Here is an example of how I did it for one variable (where seval
is a from module simple_eval
):
def get_lambda(expression, variable="x"):
return lambda arg: seval(expression.replace(variable, str(arg)))
And here's how I did it by just checking the length of the arguments*
passed:
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
EDIT: I am taking expression and variables from user input, so a safe way to do this would be best.
If you can use Python 3 then the newly introduced(Python 3.3+) inspect.Signature
and inspect.Parameter
can make your code very clean(PEP 362 - Function Signature Object). These come very handy in decorators as well:
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
Demo:
>>> 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
Will produce useful error messages for user as well:
>>> 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
Function signature for introspection purpose:
>>> inspect.getargspec(g)
ArgSpec(args=['x', 'y', 'z'], varargs=None, keywords=None, defaults=None)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With