Python3从函数返回多个上下文管理器,以便在单个with语句中使用

huangapple go评论48阅读模式
英文:

Python3 return multiple contextmanagers from a function to be used in a single with statement

问题

根据psycopg2文档https://www.psycopg.org/docs/usage.html#transactions-control,con对象管理事务并负责提交和回滚数据库事务。因此,在with语句中同时需要concon.cursor()以正确管理提交/回滚。

现在,您想要多次重复代码中的with部分,以执行多个事务,例如:

con = psycopg2.connect()

with con, con.cursor() as c:
  c.execute() # 这里有一些查询

with con, con.cursor() as c:
  c.execute() # 这里有另一个查询

...

with con, con.cursor() as c:
  c.execute() # 这里有最终的查询

这种方法有效,但需要在每个with块中复制粘贴con, con.cursor()部分。

现在,您想知道在Python中是否可以创建一个函数,返回一个可以直接传递给with语句的内容,以将con, con.cursor()简化为some_custom_function()

以下是一个示例代码:

con = psycopg2.connect()

def cursor():
  return con, con.cursor() # 这不起作用

with cursor() as c:
  c.execute() # 这里有一些查询

with cursor() as c:
  c.execute() # 这里有另一个查询

...

with cursor() as c:
  c.execute() # 这里有最终的查询

(您可能会想知道为什么要这样做,但con.cursor()方法还接受参数,如cursor_factory=psycopg2.extras.RealDictCursor。然后,我还需要在每个with语句中重复这些参数。但为了简化这个示例,我省略了这一点。)

英文:

Given:

con = psycopg2.connect()

with con, con.cursor() as c:
  c.execute() # some query inside here

According to the psycopg2 documentation https://www.psycopg.org/docs/usage.html#transactions-control, the con object manages the transaction and takes care of commit and rollback of the db transaction. So both the con and con.cursor() are required in the with statement to properly manage commit/rollback

Now I want repeat the with part of the code multiple times, to do multiple transactions, such as

con = psycopg2.connect()

with con, con.cursor() as c:
  c.execute() # some query inside here

with con, con.cursor() as c:
  c.execute() # another query inside here

...

with con, con.cursor() as c:
  c.execute() # final query inside here

This works but this requires me to copy paste the con, con.cursor() part of the with statement for every with block.

Now I was wondering if it is possible in python to create a function that returns something that I can pass directly to the with statement to reduce con, con.cursor() to some_custom_function()

Something along these lines:

con = psycopg2.connect()

def cursor():
  return con, con.cursor() # this doesn't work

with cursor() as c:
  c.execute() # some query inside here

with cursor() as c:
  c.execute() # another query inside here

...

with cursor() as c:
  c.execute() # final query inside here

(You may be wondering why, but the con.cursor() method also takes arguments such as cursor_factory=psycopg2.extras.RealDictCursor. Then I would have to repeat those arguments with every with statement as well. But for simplicity of this example, I've left that out of the question.)

答案1

得分: 3

尝试使用 contextlib.contextmanager

from contextlib import contextmanager
import psycopg2

con = psycopg2.connect(...)

@contextmanager
def with_txn_and_cursor():
    with con, con.cursor(cursor_factory=RealDictCursor) as cur:
        yield cur

with with_txn_and_cursor() as cur:
    cur.execute(...)

如果您需要同时使用 concur,从上下文管理器中返回一个元组。

@contextmanager
def with_txn_and_cursor_2():
    with con, con.cursor(cursor_factory=RealDictCursor) as cur:
        yield (con, cur)

with with_txn_and_cursor_2() as (con, cur):
    cur.execute(...)
英文:

Try contextlib.contextmanager:

from contextlib import contextmanager

import psycopg2

con = psycopg2.connect(...)


@contextmanager
def with_txn_and_cursor():
    with con, con.cursor(cursor_factory=RealDictCursor) as cur:
        yield cur


with with_txn_and_cursor() as cur:
    cur.execute(...)

If you need both the con and cur, yield a tuple out of the context manager.

@contextmanager
def with_txn_and_cursor_2():
    with con, con.cursor(cursor_factory=RealDictCursor) as cur:
        yield (con, cur)


with with_txn_and_cursor_2() as (con, cur):
    cur.execute(...)

huangapple
  • 本文由 发表于 2023年3月7日 18:07:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75660540.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定