在上下文管理器中__init__ vs __enter__

时间:2016-09-21 08:40:39

标签: python python-3.x contextmanager

据我所知,上下文管理器的__enter__()@contextmanager方法一个接一个地被调用,一个接一个地被调用,没有机会在其间执行任何其他代码。将它们分成两种方法的目的是什么,我应该在每种方法中加入什么?

编辑:抱歉,没有注意文档。

编辑2:实际上,我感到困惑的原因是因为我在想@contextmananger装饰者。使用with创建的上下文管理器只能使用一次(第一次使用后生成器将耗尽),因此通常使用with语句中的构造函数调用来编写它们;如果这是使用@contextmanager声明的唯一方法,我的问题就有意义了。当然,实际上,上下文管理器比ShowDialog可以创建的更为通用;特别是上下文管理器通常可以重用。我希望这次能说得对吗?

1 个答案:

答案 0 :(得分:36)

  

据我了解,上下文管理器的__init__()__enter__()方法一个接一个地调用一次,一个接一个地调用,而不会在其间执行任何其他代码。

你的理解是不正确的。创建对象时调用__init__,使用__enter__语句输入with时调用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

with可以单独初始化并输入多个__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__

此外,如果__enter__def open_etc_file(name): return open(os.path.join('/etc', name)) with open_etc_file('passwd'): ... 未分开,则甚至无法使用以下内容:

open

因为初始化(在with内)明显与with条目分开。

contextlib.manager创建的管理员是单一参与者,但它们可以再次在from contextlib import contextmanager @contextmanager def tag(name): print("<%s>" % name) yield print("</%s>" % name) 块之外构建。举个例子:

def heading(level=1):
    return tag('h{}'.format(level))

my_heading = heading()
print('Below be my heading')
with my_heading:
     print('Here be dragons')

您可以将其用作:

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

输出:

my_heading

但是,如果您尝试重复使用tag(以及RuntimeError: generator didn't yield ),您将获得

CREATE TABLE test (
 id int DEFAULT NULL,
 Barcode  nvarchar(20) DEFAULT NULL,
 Description nvarchar(40) DEFAULT NULL
) --ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- ----------------------------
-- Records of test
-- ----------------------------
INSERT INTO test VALUES ('1', '554412', 'Bar');
INSERT INTO test VALUES ('2', '554412', 'Stone');
INSERT INTO test VALUES ('3', '554412', 'Bar');


--  If you want to show all ids then use the STUFF() code, so it will return 1,3 for Bar and 2 for Description
SELECT Barcode, Description,
(
SELECT STUFF( (
SELECT ', ' + convert(varchar(10),b.id )
FROM test b
WHERE b.barcode = t.Barcode AND b.Description= t.Description
FOR XML PATH('') 
) , 1, 2, '')) AS Id
FROM dbo.test t
group by Barcode, Description


-- If you just want to display  1 for Bar and 2 for Description then use below code
SELECT Barcode, Description,
(
SELECT TOP 1 b.id 
FROM test b
WHERE b.barcode = t.Barcode AND b.Description= t.Description
) AS Id
FROM dbo.test t
group by Barcode, Description