调用 DB (psycopg2) 后如何获得平面列表?
How to get a flat list after making a call to DB (psycopg2)?
如果您只需要数据库中的一列,获得平面的最佳方法是什么 list/set?默认情况下 DB-API 总是需要 return 嵌套列表,例如:ids = [(1,). (2,), (3,),].
sqlite3
有 row_factory
实现,也许 psycopg2
也有类似的选项?我检查了文档但没有找到。
目前,我通过 list comprehension
解包,但这导致了另一个关于代码风格的重要问题:解包的最佳和清晰位置:
- 调用后立即
fetchall
。
- 调用一个
crud
函数后,里面使用了fetchall
。
例如:
# Crud function, var 1
def read_user_photos(id: int, cursor) -> set: # Var 1
cursor.execute('SELECT photo FROM photos WHERE id = %s', (id,))
return [photos_lst[0] for photos_lst in cursor.fetchall()]
# Some class method that uses crud, var 2
photos = [photo_lst[0] for photo_lst in read_user_photos(id, cursor)] # Var 2
do anything_with_photos(photos=photos)
在 crud
函数中解包的优缺点我看到了:
- 会让代码更清晰
- 下一个遇到此代码的程序员将期望从
crud
函数中获取数据,完全按照 DB-API 的要求,嵌套列表,而不是平面列表。
我只是确保您的文档字符串和类型符合标准——您的文档字符串和类型目前有些欠缺或有误(例如,您返回的是列表,而不是集合)。
像这样的东西会通过我的代码审查(假设 photo
在这里是一个整数,只有你能确定):)
from typing import Set
def get_user_photo_ids(cursor, *, user_id: int) -> Set[int]:
"""
Get a set of user photo IDs for a given user.
:param cursor: Database cursor.
:param user_id: User ID.
"""
cursor.execute('SELECT photo FROM photos WHERE id = %s', (user_id,))
return {row[0] for row in cursor}
此函数的用户可以很容易地针对另一个查询对其进行调整,例如(使用虚构的多语言 photo_names
table)
def get_photo_names(cursor, *, photo_ids: Iterable[int], language: str) -> Dict[int, str]:
"""
Get a mapping of photo names for ids in a given language.
:param cursor: Database cursor.
:param photo_ids: Photo IDs to retrieve names for. Consumes the iterable.
:param language: The language to retrieve the names in.
"""
cursor.execute('SELECT photo_id, name FROM photo_names WHERE photo_id IN %s AND language = %s', (tuple(photo_ids), language))
return dict(cursor)
在 psycopg2 中,您必须对游标进行子类化。在 psycopg 3 中你可以定义一个 row factory.
>>> import psycopg
>>> from psycopg.rows import args_row
>>> cnn = psycopg.connect()
>>> def first_field(*t):
... return t[0]
>>> cur = cnn.cursor(row_factory=args_row(first_field))
>>> cur.execute("select generate_series(1,5)").fetchall()
[1, 2, 3, 4, 5]
如果您只需要数据库中的一列,获得平面的最佳方法是什么 list/set?默认情况下 DB-API 总是需要 return 嵌套列表,例如:ids = [(1,). (2,), (3,),].
sqlite3
有 row_factory
实现,也许 psycopg2
也有类似的选项?我检查了文档但没有找到。
目前,我通过 list comprehension
解包,但这导致了另一个关于代码风格的重要问题:解包的最佳和清晰位置:
- 调用后立即
fetchall
。 - 调用一个
crud
函数后,里面使用了fetchall
。
例如:
# Crud function, var 1
def read_user_photos(id: int, cursor) -> set: # Var 1
cursor.execute('SELECT photo FROM photos WHERE id = %s', (id,))
return [photos_lst[0] for photos_lst in cursor.fetchall()]
# Some class method that uses crud, var 2
photos = [photo_lst[0] for photo_lst in read_user_photos(id, cursor)] # Var 2
do anything_with_photos(photos=photos)
在 crud
函数中解包的优缺点我看到了:
- 会让代码更清晰
- 下一个遇到此代码的程序员将期望从
crud
函数中获取数据,完全按照 DB-API 的要求,嵌套列表,而不是平面列表。
我只是确保您的文档字符串和类型符合标准——您的文档字符串和类型目前有些欠缺或有误(例如,您返回的是列表,而不是集合)。
像这样的东西会通过我的代码审查(假设 photo
在这里是一个整数,只有你能确定):)
from typing import Set
def get_user_photo_ids(cursor, *, user_id: int) -> Set[int]:
"""
Get a set of user photo IDs for a given user.
:param cursor: Database cursor.
:param user_id: User ID.
"""
cursor.execute('SELECT photo FROM photos WHERE id = %s', (user_id,))
return {row[0] for row in cursor}
此函数的用户可以很容易地针对另一个查询对其进行调整,例如(使用虚构的多语言 photo_names
table)
def get_photo_names(cursor, *, photo_ids: Iterable[int], language: str) -> Dict[int, str]:
"""
Get a mapping of photo names for ids in a given language.
:param cursor: Database cursor.
:param photo_ids: Photo IDs to retrieve names for. Consumes the iterable.
:param language: The language to retrieve the names in.
"""
cursor.execute('SELECT photo_id, name FROM photo_names WHERE photo_id IN %s AND language = %s', (tuple(photo_ids), language))
return dict(cursor)
在 psycopg2 中,您必须对游标进行子类化。在 psycopg 3 中你可以定义一个 row factory.
>>> import psycopg
>>> from psycopg.rows import args_row
>>> cnn = psycopg.connect()
>>> def first_field(*t):
... return t[0]
>>> cur = cnn.cursor(row_factory=args_row(first_field))
>>> cur.execute("select generate_series(1,5)").fetchall()
[1, 2, 3, 4, 5]