Python中的上下文变量

时间:2018-06-14 10:10:01

标签: python python-3.x python-3.7

假设我的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的Context Variables吗?怎么样?
  • 这就是这些上下文变量的用途吗?
  • 如何使用Python v3.6或更早版本执行此操作?

修改

由于多种原因(架构遗产,图书馆等)我无法改变f2等中间函数的签名,因此我可以&# 39; t只是将user_id作为参数传递,不会将所有这些函数放在同一个类中。

3 个答案:

答案 0 :(得分:4)

您可以在Python 3.7中使用subview来了解您的问题。这通常很容易:

override func viewDidLoad() {
    super.viewDidLoad()

    for view in setView.subviews {
        mySubViews.append(view)
        let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(getIndex(_:)))
        gestureRecognizer.delegate = self
        view.addGestureRecognizer(gestureRecognizer)
    }
}

@objc func getIndex(_ sender:UIView) {
    print(mySubViews.index(of: sender))
}

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 方法返回set实例,您可以使用该实例将变量重置为ContextVar操作发生之前的值。因此,如果您希望Token按照它们的方式恢复(对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) (或查找已由您的框架定义过的),并在其上显示contextvarsContextVar值,如上所示,您应该很高兴。

许多get的使用可能会在后台,作为各种图书馆的实施细节,希望拥有一个全球的"不会在线程之间或单个线程内的单独异步任务之间泄漏更改的状态。在这种情况下,上面的示例可能更有意义:setcontextvars是同一个库的一部分,而f1是用户提供的回调,传递到其他地方的库中。

答案 1 :(得分:3)

基本上你正在寻找的是一种在一组功能之间共享状态的方法。在面向对象语言中这样做的规范方法是使用类:

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的答案中所示。

作为一般规则:依靠全局变量或某些"魔法"可以 - 或者不是 - 由你设置的上下文 - 不管是谁也不是在哪里 - 也不是 - 什么时候使代码变得困难,如果不是不可能的推理,那可以以最不可预测的方式打破(谷歌搜索&# 34;全球邪恶"将会产生关于这个主题的大量文章。)

答案 2 :(得分:1)

您可以在函数调用中使用kwargs以传递

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, user_id=user_id)

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

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

kwargs dict等同于您在上下文变量中看到的内容,但仅限于调用堆栈。它是在每个函数中传递(通过指针式)的相同内存元素,而不是在内存中复制变量。

在我看来,但我想看看你们都在想什么,上下文变量是一种优雅的方式来授权全局变量并控制它。