起因
网上大多数都是通过functools.partial
或硬编码的方式实现柯里化。
硬编码缺点就不说了。
functools.partial
缺点是如果要柯里化需要调用partial
包装一下。
如果是改原有项目工作量会有点大。
而且partial
包装后的函数并不能在参数填满时返回代理函数的返回值。
代码
我这里用python装饰器可以无缝地把一个函数 柯里化
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
使用方式
@currying
def add(x, y, z):
"""连加"""
return x+y-z
print(add(1)(2)(7))
print(add(1, 2)(5))
print(add.__doc__)
这里特意输出了一下__doc__
。可以发现包装后的函数,无论是函数名还是文档都和原来一样。真正做到对原来代码无侵入。
支持 关键字参数
上面的currying基本够用了
但如果你想要像下面这样调用函数就不行了
@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")
让我们来改造一下代码
升级后的@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
变种
最近刚刚想到的,这个装饰器可以用来绑定上下文环境。
def bind(func):
'''绑定上下文环境'''
def wrapper(ctx):
@functools.wraps(func)
def wrapped_func(*args,**kwargs):
return func(*((ctx,)+ args),**kwargs)
return wrapped_func
return wrapper
用法
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()