如何使嵌套for循环更加Pythonic

zen*_*mer 8 python list-comprehension nested-loops

我必须为每个密钥创建一个被阻止用户的列表.每个用户都有多个属性,如果这些属性中的任何一个属于键,则会阻止用户.

我编写了以下嵌套for-loop,它适用于我,但我想以更加pythonic的方式编写它,使用更少的行和更具可读性的方式.我怎样才能做到这一点?

for key in keys:
    key.blocked_users = []

for user in get_users():
    for attribute in user.attributes:
        for key in keys:
            if attribute.name == key.name:
                key.blocked_users.append(user)
Run Code Online (Sandbox Code Playgroud)

tim*_*geb 6

在您的特定情况下,内for循环依赖于外循环变量,我会保留代码原样。您不会通过强制减少行数来使代码更加 Pythonic 或可读。

如果这些嵌套循环是直观地编写的,它们可能很容易阅读。

如果您有for带有“独立”循环变量的嵌套循环,则可以使用itertools.product。这是一个演示:

>>> from itertools import product
>>> a = [1, 2]
>>> b = [3, 4]
>>> c = [5]
>>> for x in product(a, b, c): x
... 
(1, 3, 5)
(1, 4, 5)
(2, 3, 5)
(2, 4, 5)
Run Code Online (Sandbox Code Playgroud)


MSe*_*ert 5

您可以在第一个for循环中使用条件理解:

for key in keys:
    keyname = key.name
    key.blocked_users = [user for user in get_users() if any(attribute.name == keyname for attribute in user)]
Run Code Online (Sandbox Code Playgroud)

  • 正如我所写的那样,如果它是一个昂贵的操作,它会将`users = get_users()`拉出循环.我没有创建`keyname`但它是一个小的性能改进. (2认同)

MSe*_*ert 4

除了缩短它之外,您还可以尝试减少对 Python 中优化的函数的操作。它可能不会更短,但可能会更快——还有什么比速度更Pythonic呢?:)

例如,您迭代每个用户的每个属性的键。那只是希望“远离”优化。例如,您可以收集字典中的键名称(用于查找)和集合(用于与属性名称的交集)一次:

for key in keys:
    key.blocked_users = []

keyname_map = {key.name: key.blocked_users for key in keys}  # map the key name to blocked_user list
keynames = set(keyname_map)
Run Code Online (Sandbox Code Playgroud)

set(keyname_map)是一个非常高效的操作,因此保留两个集合并不重要。

然后使用set.intersection来获取与属性名称匹配的键名:

for user in get_users():
    for key in keynames.intersection({attribute.name for attribute in user.attributes}):
        keyname_map[key].append(user)
Run Code Online (Sandbox Code Playgroud)

set.intersection也相当快。

但是,这种方法要求您的attribute.names 和key.names 是可散列的。