据我所知,上下文管理器的__enter__()
和@contextmanager
方法一个接一个地被调用,一个接一个地被调用,没有机会在其间执行任何其他代码。将它们分成两种方法的目的是什么,我应该在每种方法中加入什么?
编辑:抱歉,没有注意文档。
编辑2:实际上,我感到困惑的原因是因为我在想@contextmananger
装饰者。使用with
创建的上下文管理器只能使用一次(第一次使用后生成器将耗尽),因此通常使用with
语句中的构造函数调用来编写它们;如果这是使用@contextmanager
声明的唯一方法,我的问题就有意义了。当然,实际上,上下文管理器比ShowDialog
可以创建的更为通用;特别是上下文管理器通常可以重用。我希望这次能说得对吗?
答案 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