在我想要数据时返回生成器对象

时间:2016-10-13 00:12:36

标签: python python-3.x generator

我对Python 3非常陌生,所以请你好。

我已经查看了我能找到的关于此的所有文档 - 似乎有很多,只是我仍然无法弄清楚要做什么。

这是我的代码:

poly = [[36606.0,53223.0],[37332.0,52224.0],[39043.0,53223.0],
[41603.0,53223.0],[42657.0,53278.0],[43123.0,52060.0],
[44054.0,51156.0],[45806.0,51498.0],[46751.0,53237.0],
[46983.0,54606.0],[45861.0,57057.0],[44971.0,58836.0],
[44054.0,60616.0],[43451.0,58138.0],[41850.0,59179.0],
[40850.0,60370.0],[39906.0,59233.0],[38674.0,59179.0],
[35566.0,56249.0],[37592.0,57536.0]]

def perimeter():
     return (sum((abs((x1 - x0)**2)+abs((y1 - y0)**2))**.5)
                        for ((x0, y0), (x1, y1)) in parts1(poly))

def parts1(poly):
    return poly[0:] + [poly[0]]

print(perimeter())

正在运行没有任何错误,但我收到了返回:

generator object perimeter.locals.genexpr at 0x7f47d8671c50

如何制作它以便给我价值/答案? 我真的不确定发生了什么。

5 个答案:

答案 0 :(得分:1)

在括号中包装item for item in iterable构造使其成为一个惰性生成器表达式,具有您看到的外观。现在,它之所以这样做,而不是给你一个错误,你试图发送一个不可迭代的sum()是因为它没有评估任何东西。如果您将此生成器表达式发送到list()进行评估,则会出现错误。

你需要移动一些括号:

sum((abs((x1 - x0)**2)+abs((y1 - y0)**2))**.5
                        for ((x0, y0), (x1, y1)) in parts1(poly))

现在您有sum(expression for element in parts1(poly))而不是(sum(expression) for element in parts1(poly))

小测试:

>>> x1, x0, y1, y0 = 3, 1, 5, 4
>>> sum((abs((x1 - x0)**2)+abs((y1 - y0)**2))**.5
...     for ((x0, y0), (x1, y1)) in [((x0,y0), (x1, y1))])
2.23606797749979

答案 1 :(得分:1)

第一个问题是你的括号在错误的地方。您想在生成器表达式上调用sum(),而是使用sum()编写生成器表达式。所以你可以试试这个:

def perimeter():
    return sum(((x1 - x0) ** 2) + ((y1 - y0) ** 2) ** .5
                 for (x0, y0), (x1, y1) in parts1(poly))

(我还发出了abs()次来电,因为对一个数字进行平方会使其成为正数,使abs()无关紧要,并删除一些不必要的括号。)

但这仍然不起作用:现在你得到'float' object is not iterable

这是因为您尝试从列表的每个元素中解压缩四个值,但每个元素只包含两个。 Python将每个元素解包为两个浮点数,然后尝试将每个浮点数解包为两个变量。这就是错误信息让人头疼的地方。

因此,您需要更改parts1()以返回列表列表的列表。也就是说,列表中的每个项目都是一个列表,其中包含两个列表,每个列表包含一个点的坐标(给定点及其后继点)。一种方法是使用内置的zip()函数和列表的偏移或旋转副本。

def parts1(poly):
    return zip(poly, poly[1:] + poly[:1])

最后,您并不真正需要单独的函数parts1() - 它可以直接进入perimeter()函数。您应该将poly传递给perimeter()

def perimeter(poly):
    return sum(((x1 - x0) ** 2) + ((y1 - y0) ** 2) ** .5
                 for (x0, y0), (x1, y1) in zip(poly, poly[1:] + poly[:1]))

您可以通过遍历poly中的坐标并跟踪您看到的最后一项,而无需列表的额外副本。但你不能把它写成生成器表达式。相反,您使用常规的for循环。

def perimeter(poly):
    x0, y0 = poly[-1][0], poly[-1][1]
    per = 0.0
    for x1, y1 in poly:
        per += ((x1 - x0) ** 2) + ((y1 - y0) ** 2) ** .5
        x0, y0 = x1, y1
    return per 

不是那么简洁,也不是那么快,但没有使用尽可能多的内存。

答案 2 :(得分:0)

我会像这样尝试一些更明确的东西,这符合您的预期答案

import itertools

def calculate_length(x0, x1, y0, y1):
    return ((x1 - x0)**2+(y1 - y0)**2)**.5

def make_point_pairs(poly):
    pairs = zip(poly, poly[1:])
    # Close the shape
    chain = itertools.chain(pairs, [[poly[-1],poly[0]]])
    return chain

def perimeter(poly):
     return sum([calculate_length(x0, x1, y0, y1) for ((x0, y0), (x1, y1)) in make_point_pairs(poly)])

答案 3 :(得分:0)

您的代码有两个问题。一个是由于@ TigerhawkT3指出的括号,另一个是你没有正确地迭代这些点。以下代码解决了这两个问题。

poly = [[36606.0,53223.0],[37332.0,52224.0],[39043.0,53223.0],
        [41603.0,53223.0],[42657.0,53278.0],[43123.0,52060.0],
        [44054.0,51156.0],[45806.0,51498.0],[46751.0,53237.0],
        [46983.0,54606.0],[45861.0,57057.0],[44971.0,58836.0],
        [44054.0,60616.0],[43451.0,58138.0],[41850.0,59179.0],
        [40850.0,60370.0],[39906.0,59233.0],[38674.0,59179.0],
        [35566.0,56249.0],[37592.0,57536.0]]

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = iter(iterable), iter(iterable)
    next(b, None)
    return zip(a, b)

def parts1(poly):
    return poly[0:] + [poly[0]]

def perimeter():
    return sum((abs((x1 - x0)**2)+abs((y1 - y0)**2))**.5
                    for ((x0, y0), (x1, y1)) in pairwise(parts1(poly)))

print(perimeter())  # -> 41095.327046386046

另请注意,您可以使用内置的math.hypot()函数简化(并加快)您正在进行的计算:

import math

def perimeter2():
    return sum(math.hypot(x1-x0, y1-y0) 
                for ((x0, y0), (x1, y1)) in pairwise(parts1(poly)))

print(perimeter2())  # -> 41095.327046386046

答案 4 :(得分:0)

我对你的问题也有点困惑,似乎你不是故意使用发电机而只是想要到外围? 我认为最好稍微清理函数perimeter()并且不要在那里使用生成器,并简单地遍历列表poly并获取每个相邻的对并以这种方式计算它。

poly = [[36606.0,53223.0],[37332.0,52224.0],[39043.0,53223.0],
[41603.0,53223.0],[42657.0,53278.0],[43123.0,52060.0],
[44054.0,51156.0],[45806.0,51498.0],[46751.0,53237.0],
[46983.0,54606.0],[45861.0,57057.0],[44971.0,58836.0],
[44054.0,60616.0],[43451.0,58138.0],[41850.0,59179.0],
[40850.0,60370.0],[39906.0,59233.0],[38674.0,59179.0],
[35566.0,56249.0],[37592.0,57536.0]]

def perimeter():
    #return (sum((abs((x1 - x0)**2)+abs((y1 - y0)**2))**.5) \
                        #for ((x0, y0), (x1, y1)) in parts1(poly))
    peri=0
    for [x0, y0], [x1, y1] in get_pair(poly):
        peri+=((x1-x0)**2 + (y1-y0)**2)**0.5
    return peri

def get_pair(poly):
    i=0
    while i<len(poly):
        yield [poly [i],poly [(i+1)%len(poly)]] #The modulo takes the pair of last and first coordinates into account
        #So that when poly [i] is the last coordinate it is returned with the first coordinate
        i+=1

print(perimeter())

我为get_pair()函数使用了生成器函数,但您也可以使用循环或其他方式执行此操作。每次调用它时,它基本上只返回列表中的新对。