在 Python 3 中将双 for 循环转换为双列表理解?
Convert double for loop to double list comprehension in Python 3?
给定以下结构/逻辑:
for i in range(len(drug_list["drug_list_ids"])):
for j in range(i + 1, len(drug_list_ids["drug_list_ids"])):
with pg_get_cursor(pool) as cursor:
q = """ SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i, drugs d1, drugs d2
WHERE d1.id = %s
AND d2.id = %s
AND i.id1 = d1.id
AND i.id2 = d2.id; """
cursor.execute(q, (drug_list["drug_list_ids"][i], drug_list["drug_list_ids"][j]))
res = cursor.fetchall()
if res:
for d in res:
ddi_list.append(d)
我想把它转成双表推导,传给我
cursor.execute(q, (drug_list["drug_list_ids"][i], drug_list["drug_list_ids"][j]))
然后继续逻辑。
请告知我该怎么做?
我成功创建了第一步:
[(i,j) for i in range(len(drug_list["drug_list_ids"])) for j in range(i + 1, len(drug_list["drug_list_ids"]))]
我的字典:{'drug_list': ['dabigatran etexilate', 'dasatinib', 'lepirudin', 'atosiban', 'glycocholic acid'], 'drug_list_ids': [2, 3, 1548, 3579, 8]}
只是说清楚 - 目标是制作药物 ID 的唯一元组 (2,3)
、(2,1548)
、...、(3, 1548)
、...,而不是 (3,2)
、(1548,2)
等或类似的,并展示他们的互动。
你在这里把事情复杂化了。您不需要在循环中 运行 多个查询,只需使用 单个查询 :
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
d1.id in (... id list, see below ...)
AND d2.id = (... same id list, see below ...)
AND d1.id < d2.id
我在这里使用 INNER JOIN
语法而不是 FROM
子句中的多个表来将连接条件分组到专用位置,因此 WHERE
条件更容易推理。
以上将所有 drug_list["drug_list_ids"]
id 传递给两个 in (....)
条件,但随后将数据库限制为仅使用与 d1.id < d2.id
子句的有效组合。这会在 d1.id
和 d2.id
之间生成一整套可能的(有序的)组合,就像您的 for 循环一样,尽管具有严格的排序顺序(使用 (8, 1548)
和 (8, 3579)
而不是 (1548, 8)
和 (3579, 8)
).
Psycopg2 实际上 accepts tuples as placeholder values,并将它们扩展为正确的语法以供 ... IN ...
测试;在这种情况下,驱动程序包含括号:
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
d1.id in %s
AND d2.id in %s
AND d1.id < d2.id
"""
with pg_get_cursor(pool) as cursor:
cursor.execute(query_string, (
tuple(drug_list["drug_list_ids"]),
tuple(drug_list["drug_list_ids"])
))
ddi_list = cursor.fetchall()
或者您可以使用 Postgres ... = ANY(ARRAY[...])
test instead of ... IN ...
, and make use of the fact that psycopg2 interpolates lists as ARRAY
values:
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
d1.id = ANY(%s)
AND d2.id = ANY(%s)
AND d1.id < d2.id
"""
with pg_get_cursor(pool) as cursor:
cursor.execute(query_string, (drug_list["drug_list_ids"], drug_list["drug_list_ids"]))
ddi_list = cursor.fetchall()
如果这不可能,将循环转换为列表推导式就有点棘手了。不是因为列表理解无法处理嵌套循环(只是按嵌套顺序从左到右列出它们),而是因为您需要在循环体中使用 多个语句 来生成结果值.尽管如此,因为 psycopg2 的 cursor.execute()
always returns None
,您可以使用 cursor.execute(...) or cursor
生成下一个迭代器来循环,所以您会得到类似的东西:
[v ... for ... in outer loops ... for v in (cursor.execute(...) or cursor)]
这利用了您可以直接在游标上循环以获取行的事实。无论如何,不需要调用 cursor.fetchall()
也不需要测试是否有特定查询的结果。
您的嵌套 for
循环可以用 itertools.combinations()
:
更紧凑地表达
from itertools import combinations
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE d1.id = %s AND d2.id = %s
"""
with pg_get_cursor(pool) as cursor:
combos = combinations(drug_list["drug_list_ids"], r=2)
ddi_list = [v for id1, id2 in combos for v in (cursor.execute(query_string, (id1, id2)) or cursor)]
但是,这 一点也不高效 (将大量单独的查询发送到数据库)也不是那么可读。也不是必须的,如上图
如果您还必须更严格地控制您的 ID 配对,则必须使用嵌套元组测试;将 d1.id
和 d2.id
列放入数组中,并在右侧使用 IN ((v1, v2), (v3, v4), ...)
测试,作为元组的元组传递给 cursor.execute()
:
from itertools import combinations
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
(d1.id, d2.id) IN %s
"""
# list of [id1, id2] lists
combos = tuple(combinations(drug_list["drug_list_ids"], r=2))
with pg_get_cursor(pool) as cursor:
cursor.execute(query_string, (combos,))
ddi_list = cursor.fetchall()
给定以下结构/逻辑:
for i in range(len(drug_list["drug_list_ids"])):
for j in range(i + 1, len(drug_list_ids["drug_list_ids"])):
with pg_get_cursor(pool) as cursor:
q = """ SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i, drugs d1, drugs d2
WHERE d1.id = %s
AND d2.id = %s
AND i.id1 = d1.id
AND i.id2 = d2.id; """
cursor.execute(q, (drug_list["drug_list_ids"][i], drug_list["drug_list_ids"][j]))
res = cursor.fetchall()
if res:
for d in res:
ddi_list.append(d)
我想把它转成双表推导,传给我
cursor.execute(q, (drug_list["drug_list_ids"][i], drug_list["drug_list_ids"][j]))
然后继续逻辑。 请告知我该怎么做?
我成功创建了第一步:
[(i,j) for i in range(len(drug_list["drug_list_ids"])) for j in range(i + 1, len(drug_list["drug_list_ids"]))]
我的字典:{'drug_list': ['dabigatran etexilate', 'dasatinib', 'lepirudin', 'atosiban', 'glycocholic acid'], 'drug_list_ids': [2, 3, 1548, 3579, 8]}
只是说清楚 - 目标是制作药物 ID 的唯一元组 (2,3)
、(2,1548)
、...、(3, 1548)
、...,而不是 (3,2)
、(1548,2)
等或类似的,并展示他们的互动。
你在这里把事情复杂化了。您不需要在循环中 运行 多个查询,只需使用 单个查询 :
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
d1.id in (... id list, see below ...)
AND d2.id = (... same id list, see below ...)
AND d1.id < d2.id
我在这里使用 INNER JOIN
语法而不是 FROM
子句中的多个表来将连接条件分组到专用位置,因此 WHERE
条件更容易推理。
以上将所有 drug_list["drug_list_ids"]
id 传递给两个 in (....)
条件,但随后将数据库限制为仅使用与 d1.id < d2.id
子句的有效组合。这会在 d1.id
和 d2.id
之间生成一整套可能的(有序的)组合,就像您的 for 循环一样,尽管具有严格的排序顺序(使用 (8, 1548)
和 (8, 3579)
而不是 (1548, 8)
和 (3579, 8)
).
Psycopg2 实际上 accepts tuples as placeholder values,并将它们扩展为正确的语法以供 ... IN ...
测试;在这种情况下,驱动程序包含括号:
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
d1.id in %s
AND d2.id in %s
AND d1.id < d2.id
"""
with pg_get_cursor(pool) as cursor:
cursor.execute(query_string, (
tuple(drug_list["drug_list_ids"]),
tuple(drug_list["drug_list_ids"])
))
ddi_list = cursor.fetchall()
或者您可以使用 Postgres ... = ANY(ARRAY[...])
test instead of ... IN ...
, and make use of the fact that psycopg2 interpolates lists as ARRAY
values:
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
d1.id = ANY(%s)
AND d2.id = ANY(%s)
AND d1.id < d2.id
"""
with pg_get_cursor(pool) as cursor:
cursor.execute(query_string, (drug_list["drug_list_ids"], drug_list["drug_list_ids"]))
ddi_list = cursor.fetchall()
如果这不可能,将循环转换为列表推导式就有点棘手了。不是因为列表理解无法处理嵌套循环(只是按嵌套顺序从左到右列出它们),而是因为您需要在循环体中使用 多个语句 来生成结果值.尽管如此,因为 psycopg2 的 cursor.execute()
always returns None
,您可以使用 cursor.execute(...) or cursor
生成下一个迭代器来循环,所以您会得到类似的东西:
[v ... for ... in outer loops ... for v in (cursor.execute(...) or cursor)]
这利用了您可以直接在游标上循环以获取行的事实。无论如何,不需要调用 cursor.fetchall()
也不需要测试是否有特定查询的结果。
您的嵌套 for
循环可以用 itertools.combinations()
:
from itertools import combinations
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE d1.id = %s AND d2.id = %s
"""
with pg_get_cursor(pool) as cursor:
combos = combinations(drug_list["drug_list_ids"], r=2)
ddi_list = [v for id1, id2 in combos for v in (cursor.execute(query_string, (id1, id2)) or cursor)]
但是,这 一点也不高效 (将大量单独的查询发送到数据库)也不是那么可读。也不是必须的,如上图
如果您还必须更严格地控制您的 ID 配对,则必须使用嵌套元组测试;将 d1.id
和 d2.id
列放入数组中,并在右侧使用 IN ((v1, v2), (v3, v4), ...)
测试,作为元组的元组传递给 cursor.execute()
:
from itertools import combinations
query_string = """\
SELECT d1.name as drug_1, d2.name as drug_2, description
FROM interactions i
INNER JOIN drugs d1 ON i.id1 = d1.id
INNER JOIN drugs d2 ON i.id2 = d2.id
WHERE
(d1.id, d2.id) IN %s
"""
# list of [id1, id2] lists
combos = tuple(combinations(drug_list["drug_list_ids"], r=2))
with pg_get_cursor(pool) as cursor:
cursor.execute(query_string, (combos,))
ddi_list = cursor.fetchall()