关于python:生成器与列表理解

generator vs. list comprehension

我有一些东西,当作为列表理解运行时,运行良好。

看起来,

1
[myClass().Function(things) for things in biggerThing]

Function是一种方法,它构建一个列表。方法本身不返回任何内容,但列表在内部被操纵。

当我把它换成发电机时,

1
(myClass().Function(things) for things in biggerThing)

它不像我期望的那样操纵数据。事实上,它似乎根本无法操纵它。

列表理解和生成器之间的功能差异是什么?


发电机在使用过程中会被实时评估。因此,如果您从未在生成器上迭代,则不会对其元素进行计算。

所以,如果你这样做了:

1
2
for _ in (myClass().Function(things) for things in biggerThing):
    pass

Function将运行。

现在,你的意图还不清楚。

相反,考虑使用map

1
map(myClass().Function, biggerThing)

注意,这将始终使用MyClass的同一个实例

如果这是一个问题,那么做:

1
2
for things in BiggerThing:
    myClass().Function(things)


对生成器进行延迟评估。您需要处理一个生成器,以便对您的函数进行评估。可以使用collections.deque消耗发电机:

1
2
3
import collections
generator = (myClass().Function(thing) for thing in biggerThing)
collections.deque(generator , maxlen=0)

并考虑使用@staticmethod@classmethod或更改为

1
2
3
myfunc = myClass().Function
generator = (myfunc(thing) for thing in biggerThing)
collections.deque(generator , maxlen=0)

减少每个thing处理的myClass创建的新实例。

更新,性能

  • collectionsiteration的比较
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def l():
        for x in range(100):
           y = x**2
          yield y

    def consume(it):
        for i in it:
            pass

    >>> timeit.timeit('from __main__ import l, consume; consume(l())', number=10000)
    0.4535369873046875
    >>> timeit.timeit('from __main__ import l, collections; collections.deque(l(), 0)', number=10000)
    0.24533605575561523

  • 实例vs类vs静态方法
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class Test(object):
        @staticmethod
        def stat_pow(x):
            return x**2
        @classmethod
        def class_pow(cls, x):
            return x**2
        def inst_pow(self, x):
            return x**2

    def static_gen():
        for x in range(100):
            yield Test.stat_pow(x)

    def class_gen():
        for x in range(100):
            yield Test.class_pow(x)

    def inst_gen():
        for x in range(100):
            yield Test().inst_pow(x)

    >>> timeit.timeit('from __main__ import static_gen as f, collections; collections.deque(f(), 0)', number=10000)
    0.5983021259307861
    >>> timeit.timeit('from __main__ import class_gen as f, collections; collections.deque(f(), 0)', number=10000)
    0.6772890090942383
    >>> timeit.timeit('from __main__ import inst_gen as f, collections; collections.deque(f(), 0)', number=10000)
    0.8273470401763916


    创建生成器时,每个元素只能使用一次。就像我正在做一批饼干,我边吃边吃。它们满足它们的目的(让我高兴),但一旦你使用它们,它们就会消失。

    列表理解创建列表,它们将允许您永久(表面上)访问该数据结构。您还可以使用上面的所有列表方法(非常有用)。但其想法是创建一个实际的数据结构(为您保存数据的东西)。

    看看这篇文章:生成器与列表理解


    在您调用生成器上的next()之前,生成器不会执行该函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     >>>def f():
     ...    print 'Hello'
     >>>l = [f() for _ in range(3)]
     Hello
     Hello
     Hello
     >>>g = (f() for _ in range(3)) # nothing happens
     >>>
     >>>next(g)
     Hello

    列表理解:

    • 列表可以被索引。如。,。埃多克斯1〔10〕

    • 创建的列表可以使用任意次数。

    • 一个空列表占用72个字节,而对于每个项目的加法则额外占用8个字节。

    发电机:

    • 无法索引生成器

    • 发电机只能使用一次。

    • 生成器占用的内存要小得多(80字节)。

    请注意,如果是生成器,一旦使用它,里面的内容就会被清空。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    >>> sys.getsizeof([])
    72
    >>> list1 = [x for x in range(0, 5)]
    >>> sys.getsizeof(list1)
    136
    >>>
    >>> generator1 = (x for x in range(0,100))
    >>> sys.getsizeof(generator1)
    80
    >>> generator1 = (x for x in range(0,5))
    >>> sys.getsizeof(generator1)
    80
    >>> list(generator1)
    [0, 1, 2, 3, 4]
    >>> list(generator1)
    []
    >>>