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)
在您的特定情况下,内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)
您可以在第一个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)
除了缩短它之外,您还可以尝试减少对 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 是可散列的。