亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Python Click:全局訪問選項值

Python Click:全局訪問選項值

慕慕森 2023-11-09 21:55:46
假設我--debug/--no-debug為基本命令定義了一個標志。該標志將影響我的程序中許多操作的行為。現在我發現自己將此標志作為函數參數傳遞到各處,這看起來并不優雅。特別是當我需要在深層調用堆棧中訪問此標志時,我必須將此參數添加到堆棧上的每個函數中。我可以創建一個全局變量is_debug,并在接收該標志值的命令函數的開頭設置其值。但這對我來說似乎也不優雅。是否有更好的方法可以使用 Click 庫使某些選項值全局可訪問?
查看完整描述

2 回答

?
慕村9548890

TA貢獻1884條經驗 獲得超4個贊

有兩種方法可以實現此目的,具體取決于您的需要。他們最終都使用了 click?Context。

就我個人而言,我很喜歡選項 2,因為這樣我就不必修改函數簽名(而且我很少編寫多線程程序)。它聽起來也更像你正在尋找的東西。

選項 1:將上下文傳遞給函數

使用click.pass_context裝飾器將單擊上下文傳遞給函數。

文件:

# test1.py

import click



@click.pass_context

def 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 開始可用。

文件:

注意:這僅在您位于當前線程(與最初用于設置單擊命令的線程相同的線程)中時才有效。

# test2.py

import click



def 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


查看完整回答
反對 回復 2023-11-09
?
Qyouu

TA貢獻1786條經驗 獲得超11個贊

我想讓它更加無縫,所以我將它與修改函數的全局范圍的技巧結合起來,并提出了以下裝飾器:

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_params

def 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: 1

The 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()


查看完整回答
反對 回復 2023-11-09
  • 2 回答
  • 0 關注
  • 206 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號