Python 3 -- [s for s in 子集(S)] 和 yield

3 投票
2 回答
5693 浏览
提问于 2025-04-17 09:26

这是我在网上找到的一段代码,但它没有太多解释。我只是想知道它是怎么工作的。我对yield[s for s in subsets(S)]这部分不是很理解。任何见解都非常感谢!

def subsets(aList):

       if aList ==[]:   # base case
          yield []
       else:
          first = aList[0]
          rest  = aList[1:]
          for ss in subsets(rest):  # include first or don't in each
              yield ss                   # subset of rest
              yield [first]+ss

print ("\n testing subsets")
S = ['A','B','C','D','E']

ss = [s for s in subsets(S)]

print ("The subsets of",S,"are:")

print (ss)

2 个回答

2

要理解 yield,可以把它想象成一个返回语句,但有个特别之处就是,下次调用这个函数时,会从 yield 语句的地方继续执行。当没有更多的 yield 了,就会抛出一个 StopIteration 的异常。

下面是一个更简单的例子,可以帮助你理解:

>>> def foo():
...     for i in range(3):
...         yield i
... 
>>> x = foo()
>>> x
<generator object foo at 0x7f0cd5c30780>
>>> x.next()
0
>>> x.next()
1
>>> x.next()
2
>>> x.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

你可以把生成器当作一个可迭代的对象来使用,因为 for 循环会自动捕捉并处理那个 StopIteration 异常:

>>> x = foo()
>>> for i in x:
...     print i
... 
0
1
2
>>>

至于获取子集,有更简单的方法!
看看下面的做法:

>>> from itertools import chain, combinations
>>>
>>> def powerset(iterable):
...     s = list(iterable)
...     return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
>>>
>>> S = ['A','B','C']
>>> list(powerset(S))
[(),
 ('A',),
 ('B',),
 ('C',),
 ('A', 'B'),
 ('A', 'C'),
 ('B', 'C'),
 ('A', 'B', 'C')]
4

subsets 是一个生成器:当你调用它的时候,它会创建一个可以逐个取值的对象。每当你从这个对象请求下一个值时,它就会运行到下一个 yield 语句,并生成那个值。它还是递归的,所以当你用五个项目运行它时,它会对最后四个项目自己调用自己,依此类推。

举个例子,如果传入 ['A'],它会创建一个第二个生成器,并传入一个空列表 []。这个生成器只会返回一个空列表,然后结束。主生成器会接收到这个空列表,返回它(yield ss),然后再返回 [first]+ss,这时就是 ['A']。最终结果是:[[], ['A']]

[s for s in subsets(S)] 是一个列表推导式。它的意思和下面的代码是一样的:

ss = []
for s in subsets(S)
    ss.append(s)

在这个情况下,其实有点多余——你可以直接用 list(subsets(S)) 来达到同样的效果。列表推导式通常用在你想对一组对象做某些操作,或者想过滤这些对象的时候。

撰写回答