【问题标题】:Creating a Python list comprehension with an if and break with nested for loops使用 if 和 break 嵌套 for 循环创建 Python 列表推导
【发布时间】:2016-11-10 14:01:46
【问题描述】:

我从this 的回答中注意到代码

for i in userInput:
    if i in wordsTask:
        a = i
        break

可以用以下方式写成列表推导式:

next([i for i in userInput if i in wordsTask])

我有一个类似的问题,我想根据列表理解编写以下(从原始问题简化)代码:

 for i in xrange(N):
     point = Point(long_list[i],lat_list[i])
     for feature in feature_list:
         polygon = shape(feature['geometry'])
         if polygon.contains(point):
             new_list.append(feature['properties'])
             break

我希望每个 point 都与特征列表中的单个多边形相关联。因此,一旦找到包含该点的多边形,break 将用于移动到下一个点。因此,new_list 将恰好有 N 元素。

我把它写成一个列表理解如下:

new_list = [feature['properties'] for i in xrange(1000) for feature in feature_list if shape(feature['geometry']).contains(Point(long_list[i],lat_list[i])]

当然,这并没有考虑到if 语句中的break,因此比使用嵌套for 循环花费的时间要长得多。使用上述链接帖子中的建议(我可能不完全理解),我做到了

new_list2 = next(feature['properties'] for i in xrange(1000) for feature in feature_list if shape(feature['geometry']).contains(Point(long_list[i],lat_list[i]))

但是,new_list2 的元素少于 N 个(在我的例子中,N=1000new_list2 只有 5 个元素)

问题 1:作为列表理解,这样做是否值得?唯一的原因是我读到列表推导通常比嵌套的 for 循环快一点。拥有 200 万个数据点,每一秒都很重要。

问题 2:如果是这样,我将如何将 break 语句合并到列表理解中?

问题 3:以我的方式使用 next 时发生了什么错误?

非常感谢您的宝贵时间和热心帮助。

【问题讨论】:

  • next() 用于获取第一个列表项,并有效地丢弃列表的其余部分。理想情况下,Python 只创建您要求的项目,如果您从不尝试查看列表的其余部分,则永远不会计算出来。 new_list3 = [next(feature['properties'] for feature in feature_list if shape(feature['geometry']).contains(Point(long_list[i], lat_list[i]))) for i in xrange(1000)] 呢?

标签: python list-comprehension nested-loops break


【解决方案1】:

我不知道如果复杂的列表推导式或生成器表达式运行相同的算法(例如访问相同数量的值),它们会比嵌套循环快得多。要获得明确的答案,您可能应该尝试两种方式实施解决方案并进行测试,看看哪种方式更适合您的真实数据。

至于如何短路内部循环而不是外部循环,您需要将 next 调用放在主列表理解中,并在其中使用单独的生成器表达式:

new_list = [next(feature['properties'] for feature in feature_list
                                       if shape(feature['shape']).contains(Point(long, lat)))
            for long, lat in zip(long_list, lat_list)]

我改变了另一件事:我没有使用来自range 的索引来索引long_listlat_list,而是使用zip 并行迭代它们。

请注意,如果一遍又一遍地创建 Point 对象最终会花费太多时间,您可以通过添加另一个嵌套生成器表达式来简化该部分代码,该表达式创建点并让您将它们绑定到(可重用) 名称:

new_list = [next(feature['properties'] for feature in feature_list
                                       if shape(feature['shape']).contains(point))
            for point in (Point(long, lat) for long, lat in zip(long_list, lat_list))]

【讨论】:

    【解决方案2】:

    列表推导不一定比for 循环快。如果你有这样的模式:

    some_var = []
    for ...:
        if ...:
            some_var.append(some_other_var)
    

    那么是的,列表理解比.append()s 更快。然而,你有情有可原。一方面,在next(...) 的情况下,它实际上是一个生成器表达式,因为它周围没有[]

    • 您实际上并没有创建一个列表(因此没有使用.append())。您只得到一个值。
    • 您的生成器为xrange(N) 中的每个i 的每个功能调用一次Point(long_list[i], lat_list[i]),而循环只为每个i 调用一次。
    • 当然,您的生成器表达式不起作用。

    为什么您的生成器表达式不起作用?因为它总体上只找到第一个值。另一方面,循环为每个i 找到第一个值。你看出区别了吗?生成器表达式跳出两个循环,但 for 循环仅跳出内部循环。


    如果您想稍微提高性能,请使用 itertools.izip()(或在 Python 3 中仅使用 zip()):

    from itertools import izip
    
    for long, lat in izip(long_list, lat_list):
        point = Point(long, lat)
        ...
    

    【讨论】:

    • 非常感谢您的回复。我现在明白next 会导致两个循环中断,这就是它不起作用的原因。我也了解 izip 如何提高性能。但是,如果您查看我的原始代码,我正在做一个 .append() (new_list.append(feature['properties']); break)。你原来关于情有可原的说法仍然成立吗?
    猜你喜欢
    • 2012-02-19
    • 2013-12-14
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    • 2016-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多