Python 中的上下文变量

新手上路,请多包涵

假设我的 Python 应用程序中有一个定义某种上下文的函数 - 例如 user_id 。此函数调用不将此上下文作为函数参数的其他函数。例如:

 def f1(user, operation):
    user_id = user.id
    # somehow define user_id as a global/context variable for any function call inside this scope
    f2(operation)

def f2(operation):
    # do something, not important, and then call another function
    f3(operation)

def f3(operation):
    # get user_id if there is a variable user_id in the context, get `None` otherwise
    user_id = getcontext("user_id")
    # do something with user_id and operation

我的问题是:

  • Python 3.7 的 上下文变量 可以用于此吗?如何?
  • 这是这些上下文变量的目的吗?
  • 如何使用 Python v3.6 或更早版本执行此操作?

编辑

由于多种原因 (体系结构遗留问题、库等), 我不能/不会更改中间函数的签名,例如 f2 ,所以我不能只传递 user_id 作为参数,既不将所有这些函数放在同一个类中。

原文由 Jundiaius 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

您可以在 Python 3.7 中使用 contextvars 来满足您的要求。这通常非常简单:

 import contextvars

user_id = contextvars.ContextVar("user_id")

def f1(user, operation):
    user_id.set(user.id)
    f2()

def f2():
    f3()

def f3():
    print(user_id.get(default=None))  # gets the user_id value, or None if no value is set

The set method on the ContextVar returns a Token instance, which you can use to reset the variable to the value it had before the set 手术发生了。因此,如果您想要 f1 以原来的方式恢复事物(对于 user_id 上下文变量不是很有用,但与在 decimal 中设置精度更相关 --- 模块),你可以这样做:

 token = some_context_var.set(value)
try:
    do_stuff()    # can use value from some_context_var with some_context_var.get()
finally:
    some_context_var.reset(token)

contextvars 模块的内容远不止于此,但您几乎可以肯定不需要处理其他内容。如果您从头开始编写自己的异步框架,您可能只需要创建自己的上下文并在其他上下文中运行代码。

如果您只是 使用 现有的框架(或编写一个您希望与异步代码一起玩的库),则不需要处理这些东西。只需创建一个全局 ContextVar (或查找您的框架已经定义的一个)和 getset 值应该如上所示去。

很多 contextvars 使用可能会在后台进行,作为各种库的实现细节,这些库希望具有“全局”状态,不会泄漏线程之间或内部独立异步任务之间的更改一个线程。上面的例子在这种情况下可能更有意义: f1f3 是同一个库的一部分, f2 是用户传递的回调进入其他地方的图书馆。

原文由 Blckknght 发布,翻译遵循 CC BY-SA 4.0 许可协议

本质上,您正在寻找的是一种在一组函数之间共享状态的方法。在面向对象语言中这样做的规范方法是使用一个类:

 class Foo(object):
    def __init__(self, operation, user=None):
        self._operation = operation
        self._user_id = user.id if user else None

    def f1(self):
        print("in f1 : {}".format(self._user_id))
        self.f2()

    def f2(self):
        print("in f2 : {}".format(self._user_id))
        self.f3()

    def f3(self):
        print("in f3 : {}".format(self._user_id))

 f = Foo(operation, user)
 f.f1()

使用此解决方案,您的类实例(此处 f )是执行函数的“上下文”——每个实例都有自己的专用上下文。

等效的函数式编程是使用闭包,我不打算在这里举一个例子,因为虽然 Python 支持闭包,但它仍然是第一个并且主要是一种对象语言,因此 OO 解决方案是最明显的。

最后,干净的程序解决方案是在整个调用链中传递此上下文(可以表示为 dict 或任何类似的数据类型),如 DFE 的答案所示。

作为一般规则:依赖于全局变量或一些可以 - 或不 - 由 you-dont-who-nor-where-nor-when 设置的“魔术”上下文使得代码即使不是不可能推理也很难,这可能会以最不可预测的方式中断(谷歌搜索“globals evil”会产生大量关于该主题的文献)。

原文由 bruno desthuilliers 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题