简体   繁体   English

如何使嵌套for循环更加Pythonic

[英]How to make nested for loop more Pythonic

I have to create a list of blocked users per key. 我必须为每个密钥创建一个被阻止用户的列表。 Each user has multiple attributes and if any of these attributes are in keys, the user is blocked. 每个用户都有多个属性,如果这些属性中的任何一个属于键,则会阻止用户。

I wrote the following nested for -loop and it works for me, but I want to write it in a more pythonic way with fewer lines and more readable fashion. 我编写了以下嵌套for -loop,它适用于我,但我希望用更少的线条和更易读的方式以更加pythonic的方式编写它。 How can I do that? 我怎样才能做到这一点?

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)

You could use a conditional comprehension in your first for -loop: 您可以在第一个for -loop中使用条件理解:

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)]

Aside from making it shorter, you could try to reduce the operations to functions that are optimized in Python. 除了缩短它之外,您还可以尝试将操作减少到Python中优化的函数。 It may not be shorter but it could be faster then - and what's more pythonic than speed?. 它可能不会更短,但它可能会更快 - 而且速度比pythonic更快? :) :)

For example you iterate over the keys for each attribute of each user. 例如,您遍历每个用户的每个属性的键。 That just sreams to be optimized "away". 这只是为了“远离”而进行优化。 For example you could collect the key-names in a dictionary (for the lookup) and a set (for the intersection with attribute names) once: 例如,您可以收集字典中的键名(用于查找)和一组(用于具有属性名称的交集):

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)

The set(keyname_map) is a very efficient operation so it doesn't matter much that you keep two collections around. set(keyname_map)是一个非常有效的操作,因此保留两个集合并不重要。

And then use set.intersection to get the keynames that match an attribute name: 然后使用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 is pretty fast too. set.intersection也很快。

However, this approach requires that your attribute.name s and key.name s are hashable. 但是,此方法要求您的attribute.namekey.name是可key.name的。

In your specific case, where the inner for loops rely on the outer loop variables, I'd leave the code just as is. 在你的特定情况下,内部for循环依赖于外部循环变量,我将保持代码原样。 You don't make code more pythonic or readable by forcefully reducing the number of lines. 通过强制减少行数,您不会使代码更加pythonic或可读。

If those nested loops were intuitively written, they are probably easy to read. 如果直观地编写这些嵌套循环,它们可能很容易阅读。

If you have nested for loops with "independent" loop variables, you can use itertools.product however. 如果您使用“独立”循环变量嵌套for循环,则可以使用itertools.product Here's a demo: 这是一个演示:

>>> 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)

Try using listed for loop in list comprehension, if that's considered more Pythonic, something like: 尝试在列表理解中使用列出的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]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM