如何使嵌套的 for 循环更 Pythonic

How to make nested for loop more Pythonic

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

我写了下面的嵌套 for 循环,它对我有用,但我想以一种更 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)

尝试在列表理解中使用列出的 for 循环,如果这被认为更符合 Pythonic,例如:

[key.blocked_users.append(user) for key in keys 
        for attribute in user.attributes 
        for user in get_users() 
        if attribute.name == key.name]

您可以在第一个 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)]

在您的特定情况下,内部 for 循环依赖于外部循环变量,我将保持代码不变。您不会通过强行减少行数来使代码更具 Python 风格或可读性。

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

如果您使用 "independent" 循环变量嵌套了 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)

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

例如,您为每个用户的每个属性迭代键。那只是说要优化 "away"。例如,您可以在字典(用于查找)和集合(用于与属性名称的交集)中收集键名一次:

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)

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)

set.intersection 也很快。

但是,此方法要求您的 attribute.namekey.name 是可哈希的。