5

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

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做这件事?

4

5 回答 5

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在函数中使用,而不是返回生成器对象,但是生成器表达式很容易获得偶数值。

于 2013-08-17T23:11:46.207 回答
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()

应该有你期望的效果。

于 2013-08-17T20:48:19.327 回答
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__

于 2013-08-17T20:51:13.760 回答
1

您没有将数据传递给课堂!

__init__创建类时调用它,并且在创建类self.data时是一个空列表,因此当您尝试:[i for i in self.data if i % 2 == 0]您迭代一个空列表!

尝试这个:

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__()
于 2013-08-17T20:47:51.770 回答
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
于 2013-08-17T20:48:46.447 回答