类中的生成器表达式不会产生我期望的输出

时间:2013-08-17 20:44:25

标签: python class python-3.x generator

我想使用一些生成器表达式作为一些昂贵的数据计算的属性。如果可能的话,我希望能够做到这样的事情:

class Test:
    def __init__(self):
        self.data = []
        self.evens = (i for i in self.data if i % 2 == 0)

def __main__():
    t = Test()
    t.data = [1,2,3,4,5,6,7,8]

    for item in t.evens:
        print(item)

if __name__ == '__main__':
    __main__()

我希望看到这个打印出2 4 6 8,但这没有输出。我在这里犯了一些简单的错误吗?

我在这里尝试做的是否可行,或者我需要使用yield来做这件事吗?

5 个答案:

答案 0 :(得分:6)

虽然其他答案已正确诊断错误(您的evens生成器正在使用原始的空data列表进行初始化),但我认为建议为data设置值仍然不会做你想做的事。

目前,您只能迭代evens一次,之后就会消耗掉它。我认为你想要的是evens始终可以作为当前data值所具有的偶数值的生成器来访问。这样做的方法是使它成为property,其函数在每次调用时返回生成器:

class Test:
    def __init__(self):
        self.data = []

    @property
    def evens(self):
        return (i for i in self.data if i % 2 == 0)

用法:

>>> t = Test()
>>> t.data = [1, 2, 3, 4, 5, 6]
>>> list(t.evens)
[2, 4, 6]
>>> list(t.evens) # works more than once
[2, 4, 5]
>>> t.data = [10, 15, 20, 25, 30]
>>> list(t.evens)
[10, 20, 30]

如果你需要一个更复杂的生成器,你可以在函数中使用yield,而不是返回一个生成器对象,但生成器表达式可以很容易地获得偶数值。

答案 1 :(得分:2)

只有在初始化类的实例时才会调用生成器表达式self.evens = (i for i in self.data if i % 2 == 0)。它使用self.data初始化为空列表,因此包含该列表中所有偶数的生成器表达式为空。再次设置data时,不会再次调用生成器表达式行。

将代码更改为:

class Test:
    def __init__(self, data):
        self.data = data
    self.evens = (i for i in self.data if i % 2 == 0)

def main():
    t = Test([1,2,3,4,5,6,7,8])

    for item in t.evens:
        print(item)

if __name__ == '__main__':
    main()

应该具有你期望的效果。

答案 2 :(得分:2)

生成器推迟创建其值,但它使用创建生成器时存在的self.data值。通过稍后使用名称 self.data,它不会像您期望的那样推迟该部分。

这很容易证明:

>>> dat = range(10,20)
>>> evens = (i for i in dat if i % 2 == 0)
>>> dat = range(10)
>>> list(evens)
[10, 12, 14, 16, 18]
>>> evens = (i for i in dat if i % 2 == 0)
>>> list(evens)
[0, 2, 4, 6, 8]

每当data发生更改时,您都需要重新创建生成器,并且最初将data作为__init__方法的参数可能是有意义的。

答案 3 :(得分:1)

你没有把数据传入课堂!

创建类时调用__init__,创建类时self.data为空列表,因此当您尝试:[i for i in self.data if i % 2 == 0]迭代空列表时! / p>

试试这个:

class Test:
    def __init__(self,data):
        self.data = data
        self.evens = [i for i in self.data if i % 2 == 0]

def __main__():
    x = [1,2,3,4,5,6,7,8]
    t = Test(x)


    for item in t.evens:
        print(item)

if __name__ == '__main__':
    __main__()

答案 4 :(得分:1)

您的生成器表达式初始化为空data。将data传递给__init__方法:

class Test:
    def __init__(self, data):
        self.evens = (i for i in data if i % 2 == 0)


def __main__():
    t = Test(data=[1,2,3,4,5,6,7,8])

    for item in t.evens:
        print(item)


if __name__ == '__main__':
    __main__()

打印:

2
4
6
8