无限生成器有表达式吗?

问题:无限生成器有表达式吗?

是否有一个可以生成无限元素的简单生成器表达式?

这是一个纯粹的理论问题。此处无需“实用”答案:)


例如,很容易制作一个有限生成器:

my_gen = (0 for i in xrange(42))

但是,要制作一个无限个,我需要用虚假函数“污染”我的命名空间:

def _my_gen():
    while True:
        yield 0
my_gen = _my_gen()

在单独的文件中处理,import以后-ing不计算在内。


我也知道这itertools.repeat完全可以做到。我很好奇是否有没有这种情况的一线解决方案。

Is there a straight-forward generator expression that can yield infinite elements?

This is a purely theoretical question. No need for a “practical” answer here :)


For example, it is easy to make a finite generator:

my_gen = (0 for i in xrange(42))

However, to make an infinite one I need to “pollute” my namespace with a bogus function:

def _my_gen():
    while True:
        yield 0
my_gen = _my_gen()

Doing things in a separate file and import-ing later doesn’t count.


I also know that itertools.repeat does exactly this. I’m curious if there is a one-liner solution without that.


回答 0

for x in iter(int, 1): pass
  • 两参数iter=零参数可调用+哨兵值
  • int() 总是回来 0

因此,iter(int, 1)是一个无限的迭代器。显然,此特定主题有很多变体(尤其是一旦添加lambda到组合中)。特别注意的一个变体是iter(f, object()),因为使用新创建的对象作为哨兵值几乎可以保证无限迭代器,而与用作第一个参数的可调用对象无关。

for x in iter(int, 1): pass
  • Two-argument iter = zero-argument callable + sentinel value
  • int() always returns 0

Therefore, iter(int, 1) is an infinite iterator. There are obviously a huge number of variations on this particular theme (especially once you add lambda into the mix). One variant of particular note is iter(f, object()), as using a freshly created object as the sentinel value almost guarantees an infinite iterator regardless of the callable used as the first argument.


回答 1

itertools 提供了三个无限生成器:

我不知道标准库中的任何其他内容。


由于您要求单线运输:

__import__("itertools").count()

itertools provides three infinite generators:

I don’t know of any others in the standard library.


Since you asked for a one-liner:

__import__("itertools").count()

回答 2

您可以遍历可调用的可返回的常量,该常量始终与iter()的哨兵不同

g1=iter(lambda:0, 1)

you can iterate over a callable returning a constant always different than iter()’s sentinel

g1=iter(lambda:0, 1)

回答 3

您的操作系统可能提供可用作无限生成器的功能。例如在Linux上

for i in (0 for x in open('/dev/urandom')):
    print i

显然,这没有效率

for i in __import__('itertools').repeat(0)
    print i

Your OS may provide something that can be used as an infinite generator. Eg on linux

for i in (0 for x in open('/dev/urandom')):
    print i

obviously this is not as efficient as

for i in __import__('itertools').repeat(0)
    print i

回答 4

没有一个在内部不使用另一个定义为类/函数/生成器的无限迭代器(不是-expression,带有的函数yield)。生成器表达式始终从可迭代的迭代器中提取,除了过滤和映射其项外什么也不做。您不能只从有限的项目到无限的项目map和或者filter您需要while(或者一个for不会终止的项,这正是我们仅使用for有限迭代器所不能拥有的)。

琐事:PEP 3142在表面上是相似的,但是仔细检查似乎仍然需要该for子句(因此(0 while True)对您而言没有),即仅提供的快捷方式itertools.takewhile

None that doesn’t internally use another infinite iterator defined as a class/function/generator (not -expression, a function with yield). A generator expression always draws from anoter iterable and does nothing but filtering and mapping its items. You can’t go from finite items to infinite ones with only map and filter, you need while (or a for that doesn’t terminate, which is exactly what we can’t have using only for and finite iterators).

Trivia: PEP 3142 is superficially similar, but upon closer inspection it seems that it still requires the for clause (so no (0 while True) for you), i.e. only provides a shortcut for itertools.takewhile.


回答 5

相当丑陋和疯狂(但是非常有趣),但是您可以通过使用一些技巧从表达式构建自己的迭代器(无需根据需要“污染”您的命名空间):

{ print("Hello world") for _ in
    (lambda o: setattr(o, '__iter__', lambda x:x)
            or setattr(o, '__next__', lambda x:True)
            or o)
    (type("EvilIterator", (object,), {}))() } 

Quite ugly and crazy (very funny however), but you can build your own iterator from an expression by using some tricks (without “polluting” your namespace as required):

{ print("Hello world") for _ in
    (lambda o: setattr(o, '__iter__', lambda x:x)
            or setattr(o, '__next__', lambda x:True)
            or o)
    (type("EvilIterator", (object,), {}))() } 

回答 6

例如,也许您可​​以使用这样的装饰器:

def generator(first):
    def wrap(func):
        def seq():
            x = first
            while True:
                yield x
                x = func(x)
        return seq
    return wrap

用法(1):

@generator(0)
def blah(x):
    return x + 1

for i in blah():
    print i

用法(2)

for i in generator(0)(lambda x: x + 1)():
    print i

我认为可以进一步改善摆脱这些丑陋的状况()。但是,这取决于您希望创建的序列的复杂性。一般来说,如果可以使用函数来表达序列,那么生成器的所有复杂性和语法糖都可以隐藏在装饰器或类似装饰器的函数内部。

Maybe you could use decorators like this for example:

def generator(first):
    def wrap(func):
        def seq():
            x = first
            while True:
                yield x
                x = func(x)
        return seq
    return wrap

Usage (1):

@generator(0)
def blah(x):
    return x + 1

for i in blah():
    print i

Usage (2)

for i in generator(0)(lambda x: x + 1)():
    print i

I think it could be further improved to get rid of those ugly (). However it depends on the complexity of the sequence that you wish to be able to create. Generally speaking if your sequence can be expressed using functions, than all the complexity and syntactic sugar of generators can be hidden inside a decorator or a decorator-like function.