origin
Most of functools.partial
the currying on the Internet is achieved through or hard-coded.
Not to mention the disadvantages of hardcoding.
functools.partial
The disadvantage is that if you want to curry, you need to call and partial
wrap it.
If it is to modify the original project, the workload will be a bit heavy.
Moreover, partial
the wrapped function cannot return the return value of the proxy function when the parameters are filled.
the code
I use the python decorator here to seamlessly curry a function
def currying(func, n=None):
"""柯里化"""
n = n or func.__code__.co_argcount
@wraps(func)
# 用于保存原函数的 函数名,帮助文档
def wrapper(*args):
k = len(args)
if k == n:
return func(*args)
elif k < n:
return currying(lambda *tail: func(*(args + tail)), n - k)
elif k > n:
raise TypeError('Too many arguments:', args)
return wrapper
How to use
@currying
def add(x, y, z):
"""连加"""
return x+y-z
print(add(1)(2)(7))
print(add(1, 2)(5))
print(add.__doc__)
Here it is specially output __doc__
. It can be found that the packaged function, whether it is the function name or the document, is the same as the original. Really do not intrude on the original code.
Support for keyword arguments
The above currying is basically enough
, but if you want to call the function like the following, it will not work
@currying
def introduce(name, age, city):
print("My name is %s, %d years old, from %s" % (name, age, city))
introduce(age=3, city="New York")(name="Mike")
Let's modify the code
Upgraded @currying
def currying(func, n=None):
"""柯里化"""
n = n or func.__code__.co_argcount
@wraps(func)
# 用于保存原函数的 函数名,帮助文档
def wrapper(*args, **kwargs):
k = len(args)+len(kwargs)
if k == n:
return func(*args, **kwargs)
elif k < n:
return currying(lambda *tail, **ktail: func(*(args + tail), **dict(kwargs, **ktail)), n - k)
raise TypeError('Too many arguments:', args)
return wrapper
variant
Just thought of it recently, this decorator can be used to bind the context.
def bind(func):
'''绑定上下文环境'''
def wrapper(ctx):
@functools.wraps(func)
def wrapped_func(*args,**kwargs):
return func(*((ctx,)+ args),**kwargs)
return wrapped_func
return wrapper
usage
class T:
def __init__(self) -> None:
self.msg = 'This is a message from the T instance.'
pass
@bind
def print_msg(ctx):
print(ctx.msg)
t = T()
t.print_msg = print_msg(t)
t.print_msg()