Python Click:全局访问选项值

假设我--debug/--no-debug为基本命令定义了一个标志。该标志将影响我的程序中许多操作的行为。现在我发现自己将此标志作为函数参数传递到各处,这看起来并不优雅。特别是当我需要在深层调用堆栈中访问此标志时,我必须将此参数添加到堆栈上的每个函数中。

我可以创建一个全局变量is_debug,并在接收该标志值的命令函数的开头设置其值。但这对我来说似乎也不优雅。

是否有更好的方法可以使用 Click 库使某些选项值全局可访问?


慕慕森
浏览 146回答 2
2回答

慕村9548890

有两种方法可以实现此目的,具体取决于您的需要。他们最终都使用了 click Context。就我个人而言,我很喜欢选项 2,因为这样我就不必修改函数签名(而且我很少编写多线程程序)。它听起来也更像你正在寻找的东西。选项 1:将上下文传递给函数使用click.pass_context装饰器将单击上下文传递给函数。文件:用法:https ://click.palletsprojects.com/en/7.x/commands/#nested-handling-and-contextsAPI: https: //click.palletsprojects.com/en/7.x/api/#click.pass_context# test1.pyimport click@click.pass_contextdef some_func(ctx, bar):    foo = ctx.params["foo"]    print(f"The value of foo is: {foo}")@click.command()@click.option("--foo")@click.option("--bar")def main(foo, bar):    some_func(bar)if __name__ == "__main__":    main()$ python test1.py --foo 1 --bar "bbb"The value of foo is: 1选项2:click.get_current_context()通过 直接从当前线程中提取上下文click.get_current_context()。从 Click 5.0 开始可用。文件:用法:https ://click.palletsprojects.com/en/7.x/advanced/#global-context-accessAPI:https ://click.palletsprojects.com/en/7.x/api/#click.get_current_context注意:这仅在您位于当前线程(与最初用于设置单击命令的线程相同的线程)中时才有效。# test2.pyimport clickdef some_func(bar):    c = click.get_current_context()    foo = c.params["foo"]    print(f"The value of foo is: {foo}")@click.command()@click.option("--foo")@click.option("--bar")def main(foo, bar):    some_func(bar)if __name__ == "__main__":    main()$ python test2.py --foo 1 --bar "bbb"The value of foo is: 1

Qyouu

我想让它更加无缝,所以我将它与修改函数的全局范围的技巧结合起来,并提出了以下装饰器:def with_click_params(func):    @functools.wraps(func)    def wrapper(*args, **kwargs):        g = func.__globals__        sentinel = object()        ctx = click.get_current_context()        oldvalues = {}        for param in ctx.params:            oldvalues[param] = g.get(param, sentinel)            g[param] = ctx.params[param]        try:            return func(*args, **kwargs)        finally:            for param in ctx.params:                if oldvalues[param] is sentinel:                    del g[param]                else:                    g[param] = oldvalues[param]    return wrapper你可以像这样使用它(从@dthor的答案借用示例):@with_click_paramsdef some_func():    print(f"The value of foo is: {foo}")    print(f"The value of bar is: {bar}")@click.command()@click.option("--foo")@click.option("--bar")def main(foo, bar):    some_func()if __name__ == "__main__":    main()这是它的实际效果:$ python test2.py --foo 1 --bar "bbb"The value of foo is: 1The value of bar is: bbb注意事项:函数只能从click原始调用堆栈调用,但这是一个有意识的选择(即,您将对变量注入做出假设)。点击单元测试指南在这里应该很有用。该函数不再是线程安全的。也可以明确要注入的参数名称:def with_click_params(*params):    def wrapper(func):        @functools.wraps(func)        def inner_wrapper(*args, **kwargs):            g = func.__globals__            sentinel = object()            ctx = click.get_current_context()            oldvalues = {}            for param in params:                oldvalues[param] = g.get(param, sentinel)                g[param] = ctx.params[param]            try:                return func(*args, **kwargs)            finally:                for param in params:                    if oldvalues[param] is sentinel:                        del g[param]                    else:                        g[param] = oldvalue        return inner_wrapper    return wrapper@with_click_params("foo")def some_func():    print(f"The value of foo is: {foo}")@click.command()@click.option("--foo")@click.option("--bar")def main(foo, bar):    some_func()if __name__ == "__main__":    main()
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python