关于C++:什么是”运行时上下文”?“runtime context”?

What is a “runtime context”?

(为了更清晰地编辑)

我在读Python书(Beazley的Python基本参考),他说:

The with statement allows a series of statements to execute inside a
runtime context that is controlled by an object that serves as a context manager.

Here is an example:

1
2
3
4
5
6
with open("debuglog","a") as f:
    f.write("Debugging
"
)
    statements
    f.write("Done
"
)

他接着说:

The with obj statement accepts an optional as var specifier. If given, the value
returned by obj._ enter _() is placed into var. It is important to emphasize
that obj is not necessarily the value assigned to var.

我理解"with"关键字的作用机制:文件对象通过open返回,并且该对象可以通过块体中的f访问。我还了解将调用enter()和exit()。

但是什么是运行时上下文呢?一些低层次的细节会很好——或者,在C.的一个例子,有人能弄清什么是"语境",以及它可能如何与其他语言(C,C++)相关。我对上下文的理解是环境,例如:bash shell在所有(env显示的)shell变量的上下文中执行ls。with the with关键字-yes f对块体是可访问的,但这不只是作用域吗?例如:对于y中的x:这里x在块内没有作用域,并且在块外保留它的值-这是Beazley在谈到"运行时上下文"时的意思吗?f只在块内作用域,并在with块外失去所有的意义?他为什么说语句"在运行时上下文中执行"????这像是一个"逃避"??

我知道open返回的对象"不是……分配给var"??为什么不分配给VaR?比兹利这样说是什么意思?


with声明在PEP 343中介绍。该政治公众人物还引入了一个新术语"上下文管理器",并定义了该术语的含义。好的。

简单地说,"上下文管理器"是一个具有特殊方法函数.__enter__().__exit__()的对象。with语句保证将调用.__enter__()方法来设置在with语句下缩进的代码块,并保证在代码块退出时调用.__exit__()方法函数(无论该块如何退出;例如,如果代码引发了一个except我们仍将调用.__exit__()。好的。

http://www.python.org/dev/peps/pep-0343/好的。

http://docs.python.org/2/reference/datamodel.html?highlight=context%20管理器带语句上下文管理器好的。

with语句现在是处理具有定义良好的设置和拆卸功能的任何任务的首选方法。使用文件,例如:好的。

1
2
with open(file_name) as f:
    # do something with file

你知道文件会在你完成后正确关闭。好的。

另一个很好的例子是资源锁:好的。

1
2
with acquire_lock(my_lock):
    # do something

你知道在你拿到锁之前代码不会运行,一旦代码完成,锁就会被释放。我不经常在python中进行多线程编码,但是当我这样做时,这个语句确保锁总是被释放,即使在遇到异常时也是如此。好的。

另外,我在谷歌网上搜索了一个上下文管理器的例子,我发现了一个很漂亮的例子:一个在特定目录中执行python块的上下文管理器。好的。

http://ralsina.me/weblog/posts/bb963.html网站好的。

编辑:好的。

运行时上下文是由对.__enter__()的调用设置的,并由对.__exit__()的调用销毁的环境。在我获取锁的示例中,代码块在具有可用锁的上下文中运行。在读取文件的示例中,代码块在正在打开的文件的上下文中运行。好的。

Python内部没有任何秘密魔法。解析器中没有特殊的作用域,没有内部堆栈,也没有特殊的内容。您只需编写两个方法函数:.__enter__().__exit__(),python在特定的点为您的with语句调用它们。好的。

再看一下政治公众人物的这一部分:好的。

记住,PEP310大致提出了这种语法("var="部分是可选的):好的。

1
2
    with VAR = EXPR:
        BLOCK

大致翻译为:好的。

1
2
3
4
5
6
    VAR = EXPR
    VAR.__enter__()
    try:
        BLOCK
    finally:
        VAR.__exit__()

在这两个示例中,BLOCK都是在特定的运行时上下文中运行的代码块,由对VAR.__enter__()的调用设置,并由VAR.__exit__()删除。好的。

with声明有两个主要好处,以及它的设置方式。好的。

更具体的好处是它是"句法糖"。我更愿意写一个两行的with语句,而不是一个六行的语句序列;两行更容易写一个更短的语句,它看起来更好,更容易理解,也更容易纠正。六行对两行意味着更多的机会把事情搞砸。(在with语句之前,我通常对将文件I/O包装在try块中很马虎;我只是有时这样做。现在我总是使用with,并且总是得到异常处理。)好的。

更抽象的好处是,这给了我们一种新的方式来思考设计我们的程序。Raymond Hettinger在Pycon 2013的一次演讲中这样说:当我们编写程序时,我们会寻找可以分解为函数的公共部分。如果我们有这样的代码:好的。

1
2
3
4
5
6
7
8
9
10
11
A
B
C
D
E

F
B
C
D
G

我们可以很容易地创建一个函数:好的。

1
2
3
4
5
6
7
8
9
10
11
12
def BCD():
    B
    C
    D

A
BCD()
E

F
BCD()
G

但我们从未有过一个真正干净的方法来进行设置/拆卸。当我们有很多这样的代码时:好的。

1
2
3
4
5
6
7
8
9
10
11
A
BCD()
E

A
XYZ()
E

A
PDQ()
E

现在我们可以定义一个上下文管理器并重写上面的内容:好的。

1
2
3
4
5
6
7
8
with contextA:
    BCD()

with contextA:
    XYZ()

with contextA:
    PDQ()

所以现在我们可以考虑我们的程序,寻找可以抽象为"上下文管理器"的设置/拆卸。RaymondHettinger展示了他发明的几个新的"上下文管理器"食谱(我绞尽脑汁想给你记住一两个例子)。好的。

编辑:好的,我刚记起一个。RaymondHettinger展示了一个配方,该配方将内置于Python3.4中,用于使用with语句忽略块内的异常。请参见:https://stackoverflow.com/a/15566001/166949好的。

另外,我尽了最大努力让他明白他在说什么……如果我犯了任何错误或错报了什么,那都是我的错,而不是他。(他有时会在stackoverflow上发帖,这样他就可以看到这个消息,如果我弄错了,他可以纠正我。)好的。

编辑:您已用更多文本更新了问题。我也会具体回答。好的。

这就是Beazley在谈到"运行时上下文"时的意思吗?F只在块内作用域,并在WITH块外失去所有意义?他为什么说语句"在运行时上下文中执行"????这像是一个"逃避"??好的。

实际上,f不只是在块内的作用域。当使用with语句中的as关键字绑定名称时,名称仍绑定在块之后。好的。

"运行时上下文"是一个非正式的概念,它的意思是"由.__enter__()方法函数调用建立并由.__exit__()方法函数调用破坏的状态",我认为最好的例子是在代码运行之前获得锁。代码块在拥有锁的"上下文"中运行。好的。

我知道open返回的对象是"not…分配给var"??为什么不分配给VaR?比兹利这样说是什么意思?好的。

好吧,假设我们有一个物体,我们称它为kk实现了一个"上下文管理器",即它具有方法函数k.__enter__()k.__exit__()。现在我们这样做:好的。

1
2
with k as x:
    # do something

大卫比兹利想让你知道的是,江户十一〔10〕不一定要和江户十一〔6〕绑定。x将受k.__enter__()回报的约束。k.__enter__()可以自由返回对k本身的引用,但也可以自由返回其他内容。在这种情况下:好的。

1
2
with open(some_file) as f:
    # do something

open()的调用返回一个作为上下文管理器工作的开放文件对象,其.__enter__()方法函数实际上只返回对自身的引用。好的。

我认为大多数上下文管理器都会返回到self。因为它是一个对象,它可以有任意数量的成员变量,所以它可以方便地返回任意数量的值。但这不是必需的。好的。

例如,可能有一个上下文管理器启动在.__enter__()函数中运行的守护进程,并从.__enter__()函数返回该守护进程的进程ID号。然后,.__exit__()函数将关闭守护进程。用途:好的。

1
2
3
4
with start_daemon("parrot") as pid:
    print("Parrot daemon running as PID {}".format(pid))
    daemon = lookup_daemon_by_pid(pid)
    daemon.send_message("test")

但您也可以返回上下文管理器对象本身,其中包含您需要的任何值:好的。

1
2
3
with start_daemon("parrot") as daemon:
    print("Parrot daemon running as PID {}".format(daemon.pid))
    daemon.send_message("test")

如果我们需要守护进程的PID,我们可以将它放在对象的.pid成员中。以后如果我们需要别的东西,我们也可以把它塞进去。好的。好啊。


WITH上下文会注意到,在输入时调用__enter__方法,并且给定的var设置为__enter__返回的任何值。

在大多数情况下,这是以前处理过的对象—在文件情况下,它是—但例如,在数据库上,不是连接对象,而是返回一个光标对象。

文件示例可以这样扩展:

1
2
3
f1 = open("debuglog","a")
with f1 as f2:
    print f1 is f2

它将打印True,此时,文件对象由__enter__返回。(从它的观点来看,self)

数据库的工作方式

1
2
3
4
d = connect(...)
with d as c:
    print d is c # False
    print d, c

这里,dc完全不同:d是与数据库的连接,c是用于一个事务的光标。

with条款终止于对__exit__()的调用,该调用给出了条款的执行状态——成功或失败。在这种情况下,__exit__()方法可以适当地发挥作用。

在文件示例中,无论是否有错误,都将关闭文件。

在数据库示例中,事务通常在成功时提交,在失败时回滚。

上下文管理器可以方便地初始化和清理文件、数据库等内容。

我没有意识到C或C++中的直接对应关系。

C不知道例外的概念,所以没有例外可以在__exit__()中被捕获。C++知道异常,并且似乎有办法做SOO(看下面的评论)。