Python3 currying decorator【@currying】

origin

Most of functools.partialthe currying on the Internet is achieved through or hard-coded.
Not to mention the disadvantages of hardcoding.
functools.partialThe disadvantage is that if you want to curry, you need to call and partialwrap it.
If it is to modify the original project, the workload will be a bit heavy.
Moreover, partialthe 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()

Test Results

Guess you like

Origin blog.csdn.net/qq_45256489/article/details/121566224