Python3 柯里化装饰器【@currying】

起因

网上大多数都是通过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()

测试结果

猜你喜欢

转载自blog.csdn.net/qq_45256489/article/details/121566224