"with",上下文管理器,python:简单来说是怎么回事?
"with", context manager, python: What's going on in simple terms?
这里的 Python 编码新手来自 Java 背景。我仍然对此感到困惑:
with open(...) as f:
do_something(f)
即使在谷歌搜索和阅读这里的一些答案之后(我还是无法理解它们)。
我的理解是,有一个叫做上下文管理器的东西,它是某种包装器,包含对创建的文件的引用。关于
as f:
上面的'as'和下面的'as'一样
import numpy as np
这只是一个别名。 'f' 不是指文件,而是上下文管理器。上下文管理器,使用装饰器模式,实现打开文件的所有方法,这样我就可以像对待文件对象一样对待它(并通过调用适当的方法获取文件对象,这些方法将在文件上调用在上下文管理器中)。而且,当然,当块完成时文件将关闭(这就是重点)。
这引出了一个问题:一般来说,open() return 是一个文件(或对文件的引用)还是上下文管理器?它是 return 一般的上下文管理器吗?这就是我们一直在使用但不自知的东西吗?或者它是否 return 文件类型,除了在这个特殊的上下文中,当 return 像上下文管理器一样不同时。
这里离右边近吗?有人想澄清一下吗?
文件对象本身就是上下文管理器,因为它们有__enter__
and __exit__
方法。 with
在进入和退出上下文时通知 file
对象(分别通过调用 __enter__
和 __exit__
),这就是文件对象 "knows" 的方式关闭文件。这里不涉及包装对象;文件对象提供了这两种方法(在 Java 术语中,您可以说文件对象实现了上下文管理器接口)。
请注意 as
是 而不是 像 import module as altname
一样的别名;相反,contextmanager.__enter__()
的 return 值 被分配给目标。 fileobject.__enter__()
方法 returns self
(所以文件对象本身),为了更容易使用语法:
with open(...) as fileobj:
如果 fileobject.__enter__()
没有这样做,而是 returned None
或另一个对象,则不能内联 open()
调用;要保留对 returned 文件对象的引用,您必须先将 open()
的结果分配给变量,然后再将其用作上下文管理器:
fileobj = open(...)
with fileobj as something_enter_returned:
fileobj.write()
或
fileobj = open(...)
with fileobj: # no as, ignore whatever fileobj.__enter__() produced
fileobj.write()
请注意,没有什么能阻止您在自己的代码中使用后一种模式;如果您已经有另一个对文件对象的引用,或者根本不需要进一步访问文件对象,那么您 没有 在此处使用 as target
部分。
但是,其他上下文管理器可以 return 不同的东西。一些数据库连接器 return 一个数据库游标:
conn = database.connect(....)
with conn as cursor:
cursor.execute(...)
并且退出上下文会导致事务被提交或回滚(取决于是否存在异常)。
上下文管理器是相当简单的野兽......它们只是 类 定义 two separate methods (__enter__
and __exit__
)。当执行 with
语句时,从 __enter__
返回的任何内容都绑定在 with
语句的 as x
子句中。
这是一个非常愚蠢的例子:
>>> class CM(object):
... def __enter__(self):
... print('In __enter__')
... return 'Hello world'
... def __exit__(self, *args):
... print('In __exit__')
...
>>> with CM() as x:
... print(x)
...
In __enter__
Hello world
In __exit__
你会经常看到上下文管理器只是从 __enter__
方法返回 self
,但我写上面的例子是为了证明你没有 [=48] =] 到。另外注意在with
语句中不需要构造context manager,可以提前构造:
cm = CM()
with cm as x:
...
上下文管理器的原因是当与with
语句一起使用时,python保证__exit__
将被调用(即使[=内部发生异常16=]套房)1.
file
对象是使用上下文管理器 API 实现的(它们具有明确定义的 __enter__
和 __exit__
方法)所以 file
对象 是个上下文管理器。当与 with
语句一起使用时,python 保证当 with
套件退出时,文件将被关闭。
1除非出现灾难性的系统故障——例如如果你的电脑爆炸了...
这是您可以创建的最基本的上下文管理器:
class UselessContextManager(object):
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
pass
with UselessContextManager() as nothing:
print(nothing is None)
如果您想稍微了解一下实际流程的样子,请试试这个:
class PrintingContextManager(object):
def __init__(self, *args, **kwargs):
print('Initializing with args: {} and kwargs: {}'.format(args, kwargs))
def __enter__(self):
print('I am entering the context')
print('I am returning 42')
return 42
def __exit__(self, type, value, traceback):
print('And now I am exiting')
print('Creating manager')
manager = PrintingContextManager()
print('Entering with block')
with manager as fnord:
print('Fnord is {}'.format(fnord))
print('End of context')
print('Out of context')
输出:
Creating manager
Initializing with args: () and kwargs: {}
Entering with block
I am entering the context
I am returning 42
Fnord is 42
End of context
And now I am exiting
Out of context
您应该尝试修改代码以打印出 type, value, traceback
,然后在 with
块内引发异常。
如您所见,with
语法 几乎 只是以下内容的缩写:
thing = ContextManager()
try:
stuff = thing.__enter__()
except Exception as e:
stuff.__exit__(type(e), e.args[0], e.__traceback__)
Though truthfully it's a bit different
您可以看到文件始终是上下文管理器:
>>> f = open('/tmp/spanish_inquisition.txt', 'w')
>>> f.__enter__
<function TextIOWrapper.__enter__>
>>> f.__exit__
<function TextIOWrapper.__exit__>
I didn't know a File could be a ContextManager simply by implementing two methods, without inheriting from a super class or explicitly implementing an interface. Again, I'm new to this language.
在Python中是显式实现一个接口。在 Java 中,您必须指定要遵守的接口。在Python中,你就去做吧。需要一个类似文件的对象?添加一个 .read()
方法。也许 .seek()
、.open()
和 .close()
取决于他们的期望。但是在 Python...
it = DecoyDuck()
if it.walks_like_a_duck() and it.talks_like_a_duck() and it.quacks_like_a_duck():
print('It must be a duck')
这里的 Python 编码新手来自 Java 背景。我仍然对此感到困惑:
with open(...) as f:
do_something(f)
即使在谷歌搜索和阅读这里的一些答案之后(我还是无法理解它们)。
我的理解是,有一个叫做上下文管理器的东西,它是某种包装器,包含对创建的文件的引用。关于
as f:
上面的'as'和下面的'as'一样
import numpy as np
这只是一个别名。 'f' 不是指文件,而是上下文管理器。上下文管理器,使用装饰器模式,实现打开文件的所有方法,这样我就可以像对待文件对象一样对待它(并通过调用适当的方法获取文件对象,这些方法将在文件上调用在上下文管理器中)。而且,当然,当块完成时文件将关闭(这就是重点)。
这引出了一个问题:一般来说,open() return 是一个文件(或对文件的引用)还是上下文管理器?它是 return 一般的上下文管理器吗?这就是我们一直在使用但不自知的东西吗?或者它是否 return 文件类型,除了在这个特殊的上下文中,当 return 像上下文管理器一样不同时。
这里离右边近吗?有人想澄清一下吗?
文件对象本身就是上下文管理器,因为它们有__enter__
and __exit__
方法。 with
在进入和退出上下文时通知 file
对象(分别通过调用 __enter__
和 __exit__
),这就是文件对象 "knows" 的方式关闭文件。这里不涉及包装对象;文件对象提供了这两种方法(在 Java 术语中,您可以说文件对象实现了上下文管理器接口)。
请注意 as
是 而不是 像 import module as altname
一样的别名;相反,contextmanager.__enter__()
的 return 值 被分配给目标。 fileobject.__enter__()
方法 returns self
(所以文件对象本身),为了更容易使用语法:
with open(...) as fileobj:
如果 fileobject.__enter__()
没有这样做,而是 returned None
或另一个对象,则不能内联 open()
调用;要保留对 returned 文件对象的引用,您必须先将 open()
的结果分配给变量,然后再将其用作上下文管理器:
fileobj = open(...)
with fileobj as something_enter_returned:
fileobj.write()
或
fileobj = open(...)
with fileobj: # no as, ignore whatever fileobj.__enter__() produced
fileobj.write()
请注意,没有什么能阻止您在自己的代码中使用后一种模式;如果您已经有另一个对文件对象的引用,或者根本不需要进一步访问文件对象,那么您 没有 在此处使用 as target
部分。
但是,其他上下文管理器可以 return 不同的东西。一些数据库连接器 return 一个数据库游标:
conn = database.connect(....)
with conn as cursor:
cursor.execute(...)
并且退出上下文会导致事务被提交或回滚(取决于是否存在异常)。
上下文管理器是相当简单的野兽......它们只是 类 定义 two separate methods (__enter__
and __exit__
)。当执行 with
语句时,从 __enter__
返回的任何内容都绑定在 with
语句的 as x
子句中。
这是一个非常愚蠢的例子:
>>> class CM(object):
... def __enter__(self):
... print('In __enter__')
... return 'Hello world'
... def __exit__(self, *args):
... print('In __exit__')
...
>>> with CM() as x:
... print(x)
...
In __enter__
Hello world
In __exit__
你会经常看到上下文管理器只是从 __enter__
方法返回 self
,但我写上面的例子是为了证明你没有 [=48] =] 到。另外注意在with
语句中不需要构造context manager,可以提前构造:
cm = CM()
with cm as x:
...
上下文管理器的原因是当与with
语句一起使用时,python保证__exit__
将被调用(即使[=内部发生异常16=]套房)1.
file
对象是使用上下文管理器 API 实现的(它们具有明确定义的 __enter__
和 __exit__
方法)所以 file
对象 是个上下文管理器。当与 with
语句一起使用时,python 保证当 with
套件退出时,文件将被关闭。
1除非出现灾难性的系统故障——例如如果你的电脑爆炸了...
这是您可以创建的最基本的上下文管理器:
class UselessContextManager(object):
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
pass
with UselessContextManager() as nothing:
print(nothing is None)
如果您想稍微了解一下实际流程的样子,请试试这个:
class PrintingContextManager(object):
def __init__(self, *args, **kwargs):
print('Initializing with args: {} and kwargs: {}'.format(args, kwargs))
def __enter__(self):
print('I am entering the context')
print('I am returning 42')
return 42
def __exit__(self, type, value, traceback):
print('And now I am exiting')
print('Creating manager')
manager = PrintingContextManager()
print('Entering with block')
with manager as fnord:
print('Fnord is {}'.format(fnord))
print('End of context')
print('Out of context')
输出:
Creating manager
Initializing with args: () and kwargs: {}
Entering with block
I am entering the context
I am returning 42
Fnord is 42
End of context
And now I am exiting
Out of context
您应该尝试修改代码以打印出 type, value, traceback
,然后在 with
块内引发异常。
如您所见,with
语法 几乎 只是以下内容的缩写:
thing = ContextManager()
try:
stuff = thing.__enter__()
except Exception as e:
stuff.__exit__(type(e), e.args[0], e.__traceback__)
Though truthfully it's a bit different
您可以看到文件始终是上下文管理器:
>>> f = open('/tmp/spanish_inquisition.txt', 'w')
>>> f.__enter__
<function TextIOWrapper.__enter__>
>>> f.__exit__
<function TextIOWrapper.__exit__>
I didn't know a File could be a ContextManager simply by implementing two methods, without inheriting from a super class or explicitly implementing an interface. Again, I'm new to this language.
在Python中是显式实现一个接口。在 Java 中,您必须指定要遵守的接口。在Python中,你就去做吧。需要一个类似文件的对象?添加一个 .read()
方法。也许 .seek()
、.open()
和 .close()
取决于他们的期望。但是在 Python...
it = DecoyDuck()
if it.walks_like_a_duck() and it.talks_like_a_duck() and it.quacks_like_a_duck():
print('It must be a duck')