Python __在上下文管理器中初始化与输入

Python __在上下文管理器中初始化与输入,python,python-3.x,contextmanager,Python,Python 3.x,Contextmanager,据我所知,上下文管理器的\uuuu init\uuuuuuuuuu()和\uuuuuuuuu enter\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu()方法被一个接一个地精确调用,在这两个。将它们分成两种方法的目的是什么?我应该在每种方法中加入什么 编辑:对不起,我没有注意文档 编辑2:事实上,我之所以困惑是因为我想到了@contextmanager装饰器。使用@contextmananger创建的上下文管理器只能使用一次(生成器在第一次

据我所知,上下文管理器的
\uuuu init\uuuuuuuuuu()
\uuuuuuuuu enter\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu()
方法被一个接一个地精确调用,在这两个。将它们分成两种方法的目的是什么?我应该在每种方法中加入什么

编辑:对不起,我没有注意文档

编辑2:事实上,我之所以困惑是因为我想到了
@contextmanager
装饰器。使用
@contextmananger
创建的上下文管理器只能使用一次(生成器在第一次使用后将耗尽),因此它们通常是在
中使用
语句的构造函数调用编写的;如果这是将
语句结合使用的唯一方法,那么我的问题就有意义了。当然,在现实中,上下文管理器比
@contextmanager
所能创建的更一般;特别是,上下文管理器通常可以重用。我希望这次我做对了

据我所知,上下文管理器的
\uuuu init\uuuuuuuuuu()
\uuuuuuuuu enter\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu()
方法被一个接一个地精确调用,在这两个

你的理解是错误的
\uuuu init\uuuu
是在创建对象时调用的,而
\uuuuu enter\uuuu
是在使用
with
语句输入对象时调用的,这是两件截然不同的事情。通常情况下,构造函数是在
初始化时直接调用的,没有中间代码,但情况并非如此

考虑这个例子:

class Foo:
    def __init__(self):
        print('__init__ called')
    def __enter__(self):
        print('__enter__ called')
        return self
    def __exit__(self, *a):
        print('__exit__ called')

myobj = Foo()

print('\nabout to enter with 1')
with myobj:
    print('in with 1')

print('\nabout to enter with 2')
with myobj:
    print('in with 2')
myobj
可以单独初始化,并使用
块在多个
中输入:

输出:

__init__ called

about to enter with 1
__enter__ called
in with 1
__exit__ called

about to enter with 2
__enter__ called
in with 2
__exit__ called
__init__ called: 42
__enter__ called
    do something with 42
__exit__ called
此外,如果
\uuuu init\uuuu
\uuuu enter\uuuuu
没有分开,甚至无法使用以下内容:

def open_etc_file(name):
    return open(os.path.join('/etc', name))

with open_etc_file('passwd'):
    ...
因为初始化(在
open
中)与带有
条目的
是明确分开的


由创建的管理者是单一进入者,但它们也可以在
块之外构建。例如:

from contextlib import contextmanager

@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)
输出:

Below be my heading
<h1>
Here be dragons
</h1>

安蒂·哈帕拉斯的回答很好。我只是想详细说明一下参数的用法(比如
myClass(*args)
),因为我不太清楚(我问自己为什么……)

with
语句中使用参数初始化类与以常规方式使用类没有什么不同。 呼叫将按以下顺序进行:

  • \uuuu init\uuuu
    (类的分配)
  • \uuuu输入\uuuu
    (输入上下文)
  • \uuuu退出
    (离开上下文)
  • 简单的例子:

    class Foo:
        def __init__(self, i):
            print('__init__ called: {}'.format(i))
            self.i = i
        def __enter__(self):
            print('__enter__ called')
            return self
        def do_something(self):
            print('do something with {}'.format(self.i))
        def __exit__(self, *a):
            print('__exit__ called')
    
    with Foo(42) as bar:
        bar.do_something()
    
    输出:

    __init__ called
    
    about to enter with 1
    __enter__ called
    in with 1
    __exit__ called
    
    about to enter with 2
    __enter__ called
    in with 2
    __exit__ called
    
    __init__ called: 42
    __enter__ called
        do something with 42
    __exit__ called
    

    如果您想确保您的调用(几乎)只能在上下文中使用(例如,强制调用
    \uuuu退出\uuuu
    ),请参阅stackoverflow帖子。在评论中,您还可以找到如何使用参数的问题的答案。

    您将创建上下文管理器与输入上下文混为一谈。这两者是不同的,您可以多次使用同一个上下文管理器。哦,天哪,我记得的所有示例都是在
    with
    语句中调用构造函数的(例如,
    with Foo()…
    )。现在一切都有意义了。ThxOh等待,那么
    @contextmanager
    呢?由于它依赖于生成器,它不是在第一次使用时就耗尽了它,从而使对象的重复使用变得不可能吗?因此可以正确地说,使用
    @contextmanager
    decorator只能生成有用的上下文管理器的子集;其余部分需要显式编写一个符合context manager API的类?请仔细使用这些措辞<代码>\uuuu init\uuuu
    实际上不是构造函数,而是Python中的启动器
    \uuuu new\uuuu
    是构造函数。您可以在
    \uuuu init\uuuu
    的方法签名中看到,如果将“self”作为第一个参数传递,则构造函数已经被调用,否则您将没有“self”的实例。