代码之家  ›  专栏  ›  技术社区  ›  kingledion

如何避免在列表理解中运行致命函数30000次?

  •  4
  • kingledion  · 技术社区  · 9 年前

    我的第一次尝试是:

    records = [(a, deadly_func(b)) for a, b in zip(listA, listB) if a == "condition"]
    

    records = [(a, i) for a, i in zip(listA, range(len(listB)) if a == "condition"]
    records = [(a, deadly_func(listB[i]) for a, i in records] 
    

    为什么我的第一次尝试没有成功?有没有一种更像蟒蛇的方式来实现这一点?


    编辑:感谢您的回复。这是两个版本的实际代码

    import shapefile, shapely.geometry as shpgeo
    
    lat = 42.3968243
    lon = -71.0313479
    
    sf = shapefile.Reader("/opt/ziplfs/tl_2014_us_zcta510.shp")
    
    records = [(r[0], shpgeo.shape(s.__geo_interface__)) for r, s in zip(sf.records(), sf.shapes()) if haversine(lon, lat, float(r[8]), float(r[7])) < 10]
    

    haversine()是一个用户制作的haversine函数,包含两对lat和long,返回以公里为单位的距离。

    from math import sqrt, sin, cos, radians, asin
    def haversine(lon1, lat1, lon2, lat2):
        """
        Calculate the great circle distance between two points 
        on the earth (specified in decimal degrees). Return is in kilometers
        """
        # convert decimal degrees to radians 
        lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
    
        # haversine formula 
        dlon = lon2 - lon1 
        dlat = lat2 - lat1 
        a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
        c = 2 * asin(sqrt(a)) 
        r = 6371 # Radius of earth in kilometers. Use 3956 for miles
        return c * r
    

    形状文件(“tl\u 2014\u us\u zcta510.shp”)是美国人口普查局提供的所有zipcodes。下载 here 如果你真的喜欢形状文件,并且你的硬盘上有800 MB,你不知道该怎么办。

    这个脚本应该返回一个元组列表,代表美国所有的zipcodes,其质心位于马萨诸塞州切尔西市10公里以内。

    对于工作版本,将记录行替换为:

    records = [(r[0], i) for r, i in zip(sf.records(), range(len(sf.records()))) if haversine(lon, lat, float(r[8]), float(r[7])) < 10]
    shapes = [shpgeo.shape(sf.shape(i).__geo_interface__) for r, i in records]
    

    $ python test.py 
    Time Elapsed: 0:00:14.221533
    $ python test.py 
    Time Elapsed: 0:00:14.637827
    $ python test.py 
    Time Elapsed: 0:00:14.253425
    

    以及工作版本:

    $ python test.py 
    Time Elapsed: 0:00:01.887987
    $ python test.py 
    Time Elapsed: 0:00:01.886635
    $ python test.py 
    Time Elapsed: 0:00:01.982547
    

    也许不是说“致命的”,但当你重复3万次时意义重大。

    2 回复  |  直到 9 年前
        1
  •  6
  •   trent oli_obk    9 年前

    不 跑 deadly_func listB . 只有那些对应的 listA True :

    listA = [True, False, True, False]
    listB = [1, 2, 3, 4]
    
    def deadly_func(x):
        print("Called with {}".format(x))
        return x
    
    print([(a, deadly_func(b)) for a, b in zip(listA, listB) if a])
    
    # Output:
    # Called with 1
    # Called with 3
    # [(True, 1), (True, 3)]
    

    编辑

    根据最新的问题,我的猜测是 sf.shapes() sf.shape(i) 仅在所需元素的子集上更有效。

    如果我的猜测是正确的,这应该可以做到:

    records = [(r[0], shpgeo.shape(sf.shape(i).__geo_interface__)) for i, r in enumerate(sf.records()) if haversine(lon, lat, float(r[8]), float(r[7])) < 10]
    

        2
  •  1
  •   Godron629    9 年前

    如果你在理解列表方面有困难,有时更容易将其分解为 for loop

    例如:

    things = []
    
    for a, b in zip(listA, listB):
        if a == "condition":
            things.append(a, deadly_func(b))
    

    此外,您可以通过添加以下内容将调试输入的大小减少到前1000个:

    for a, b, in zip(listA, listB)[:1000]:
    ....