发电机的懒惰评估

时间:2013-05-08 10:29:47

标签: python generator

考虑以下功能;

def myfunc():
    a=b=c=0
    x='12'
    a,b,c=(i for i in x)
    return a,b,c

此函数抛出以下异常:ValueError:需要多于2个值才能解压缩。 我的意图是将'x'变量中的可用值分配给给定顺序的左侧变量。因此a = 1,b = 2,c = 3,我想做什么。

为了提高我对发电机的理解,我拆解了功能

>>> dis.dis(myfunc)
  2           0 LOAD_CONST               1 (0)
              3 DUP_TOP             
              4 STORE_FAST               0 (a)
              7 DUP_TOP             
              8 STORE_FAST               1 (b)
             11 STORE_FAST               2 (c)

  3          14 LOAD_CONST               2 ('12')
             17 STORE_FAST               3 (x)

  4          20 LOAD_CONST               3 (<code object <genexpr> at 0x297b430, file "<stdin>", line 4>)
             23 MAKE_FUNCTION            0
             26 LOAD_FAST                3 (x)
             29 GET_ITER            
             30 CALL_FUNCTION            1
             33 UNPACK_SEQUENCE          3
             36 STORE_FAST               0 (a)
             39 STORE_FAST               1 (b)
             42 STORE_FAST               2 (c)

  5          45 LOAD_FAST                0 (a)
             48 LOAD_FAST                1 (b)
             51 LOAD_FAST                2 (c)
             54 BUILD_TUPLE              3
             57 RETURN_VALUE   

我猜是UNPACK_SEQUENCE正在抛出异常。是否可以在UNPACK_SEQUENCE之前进行STORE_FAST? 希望我的问题有道理。

1 个答案:

答案 0 :(得分:2)

您使用解包分配三个不同的变量。解包来迭代右手序列,以便这样做。

UNPACK_SEQUENCE确实抛出异常,因为你的生成器表达式只产生两个值,字符串'1''2',但你的左手名单列表中有 3个名称。

这对发电机来说不是问题。这是元组分配的限制,explicitly documented

  
      
  • 如果目标列表是以逗号分隔的目标列表:对象必须是与目标列表中的目标具有相同项目数的可迭代对象,并且项目将从左到右分配到相应的目标。
  •   

使用列表时会抛出相同的错误:

a, b, c = list(x)
除非x长度为3,否则

会抛出相同的异常,例如x = '123'

您可以自由地将生成器表达式分配给一个变量:

>>> x = '12'
>>> a = (i for i in x)
>>> a
<generator object <genexpr> at 0x105046dc0>

如果您希望c保持设置为0,因为生成器没有提供足够的值,那么这不是元组分配的工作方式。

您可以使用具有默认值的辅助函数:

def helper(a=0, b=0, c=0):
    return a, b, c

a, b, c = helper(*(i for i in x))

演示:

>>> def helper(a=0, b=0, c=0):
...     return a, b, c
... 
>>> a, b, c = helper(*(i for i in x))
>>> a, b, c
('1', '2', 0)

辅助函数使用所有参数的默认值,*调用语法使用生成器将这些作为位置参数处理。