如何提高许多附加到列表的性能?

2 python algorithm postgresql async-await aiopg

如何提高以下代码的性能?

BANNED_DOORBOTS = {...}

async def execute_query(self, query):
    async with self.pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(query)
            records = []
            async for row in cur:
                if row[0] not in BANNED_DOORBOTS:
                    records.append({
                        'key1': row[0],
                        'key2': row[1]
                    })

    return records
Run Code Online (Sandbox Code Playgroud)

我不想每次检查if row[0] not in BANNED_DOORBOTS.怎么避免这个?

通常,我有超过一百(最多20 000)个元素records.也许我可以预先分配一些空间以避免重新分配?

Mar*_*ers 5

您每次都从数据库查询重建列表.

我要求数据库不要返回被禁止的记录:

from psycopg2 import sql  # safe SQL composing

# Add a NOT IN clause to filter out banned doorbots, generating a
# separate parameter placeholder per value
query = sql.SQL(query) + sql.SQL(' WHERE ding_id NOT IN ({})').format(
    sql.SQL(', ').join([sql.Placeholder()] * len(BANNED_DOORBOTS)))
await cur.execute(query, BANNED_DOORBOTS)
Run Code Online (Sandbox Code Playgroud)

我使用psycopg.sql框架在这里进行合成,但你也可以使用字符串格式('%s'用于占位符).

考虑将该BANNED_DOORBOTS集合放在数据库中的表中,以便您可以使用WHERE ding_id NOT IN (SELECT id from BANNED_DOORBOTS WHERE id IS NOT NULL)子查询.这样你仍然可以获得更好的性能(数据库可以针对此进行优化),而且您不必生成占位符.

接下来,使用列表推导来构建列表.这更快,因为它避免了重复的list.append查找和方法调用.将列名称定义为元组,并将其与每行一起压缩:

keys = ('ding_id', 'doorbot_id', 'created_at', 'address', 'latitude', 
        'longitude', 'ding_kind')
return [dict(zip(keys, row)) async for row in cur]
Run Code Online (Sandbox Code Playgroud)

async for列表中理解语法需要Python 3.6或更高版本.

aiopg驱动程序允许您配置替代光标工厂,一个是已经产生的字典,这可能是越飞越快.然后,您根本不必使用任何列表理解:

from psycopg2.extras import RealDictCursor

# configure cursor to yield dictionaries rather than tuples
async with conn.cursor(cursor_factory=RealDictCursor) as cur:
    await cur.execute(query, BANNED_DOORBOTS)
    # directly return the cursor; have the caller do the async iteration
    return cur
Run Code Online (Sandbox Code Playgroud)

如果您不想让调用者负责循环,但必须生成列表,请使用该cursor.fetchall()方法生成该列表; 每个元素都是一个字典:

# configure cursor to yield dictionaries rather than tuples
async with conn.cursor(cursor_factory=RealDictCursor) as cur:
    await cur.execute(query, BANNED_DOORBOTS)
    return await cur.fetchall()
Run Code Online (Sandbox Code Playgroud)