我正在尝试创建一个类方法,可以在执行后运行一些代码。
在pytest
中,我们对fixtures
有这个功能:
@pytest.fixture
def db_connection(conn_str: str):
connection = psycopg2.connect(conn_str)
yield connection
connection.close() # this code will be executed after the test is done
在某些测试中使用此夹具可以保证在测试结束后很快关闭连接。此行为在这里的Teardown部分中进行了描述。
当我尝试在我自己的类方法中这样做时,我没有得到相同的结果。
class Database:
def __call__(self, conn_str: str):
conn = psycopg2.connect(conn_str)
yield conn
print("Got here")
conn.close()
database = Database()
conn = next(database())
cur = conn.cursor()
cur.execute("select * from users")
result = cur.fetchall()
conn.commit()
result
输出是users表中的数据,但是我从来没有看到"Got here"字符串,所以我猜yield
关键字之后的代码永远不会运行。
有办法做到这一点吗?
你要做的是实现一个上下文管理器;与Pytext fixture的类似是偶然的。
可以使用contextmanager.contextlib
from contextlib import contextmanager
@contextmanager
def db_connection(conn_str):
connection = psycopg2.connect(conn_str)
yield connection
connection.close()
with db_connection(...) as db:
...
或显式定义Database.__enter__
和Database.__exit__
:
class Database:
def __init__(self, conn_str: str):
self.conn_str = conn_str
def __enter__(self):
self.conn = psycopg2.connect(self.conn_str)
return self.conn
def __exit__(self, *args):
print("Got here")
self.conn.close()
with Database(...) as db:
...
(您可以使用psycopg2.connect
返回的连接作为上下文管理器本身)
您需要另一个next
调用来让它在yield之后运行代码:
database = Database()
gen = database() # Saved the generator to a variable
conn = next(gen)
cur = conn.cursor()
cur.execute("select * from users")
result = cur.fetchall()
conn.commit()
next(gen) # Triggers the latter part of the function
还要注意,当您耗尽一个生成器时,它会引发一个StopIteration
异常。你也需要抓住这个