Python中意外的列表推导行为
我觉得我遇到了一些复杂的嵌套作用域规则和列表推导式的问题。Jeremy Hylton的博客提到了一些可能的原因,但我对CPython的实现了解得不够深,无法找到解决办法。
这里有一个(可能有点复杂的)例子。如果有人有更简单的例子来演示这个问题,我很想听听。问题是:使用next()的列表推导式最后的结果都是来自最后一次迭代的值。
编辑: 问题:
这到底是怎么回事,我该如何解决?我需要使用标准的for循环吗?显然这个函数运行的次数是正确的,但列表推导式的结果却是最后的值,而不是每次循环的结果。
一些假设:
- 生成器?
- 列表推导式的懒加载?
代码
import itertools
def digit(n):
digit_list = [ (x,False) for x in xrange(1,n+1)]
digit_list[0] = (1,True)
return itertools.cycle ( digit_list)
>>> D = digit(5) >>> [D.next() for x in range(5)] ## This list comprehension works as expected [(1, True), (2, False), (3, False), (4, False), (5, False)]
class counter(object):
def __init__(self):
self.counter = [ digit(4) for ii in range(2) ]
self.totalcount=0
self.display = [0,] * 2
def next(self):
self.totalcount += 1
self.display[-1] = self.counter[-1].next()[0]
print self.totalcount, self.display
return self.display
def next2(self,*args):
self._cycle(1)
self.totalcount += 1
print self.totalcount, self.display
return self.display
def _cycle(self,digit):
d,first = self.counter[digit].next()
#print digit, d, first
#print self._display
self.display[digit] = d
if first and digit > 0:
self._cycle(digit-1)
C = counter()
[C.next() for x in range(5)]
[C.next2() for x in range(5)]
输出
In [44]: [C.next() for x in range(6)] 1 [0, 1] 2 [0, 2] 3 [0, 3] 4 [0, 4] 5 [0, 1] 6 [0, 2] Out[44]: [[0, 2], [0, 2], [0, 2], [0, 2], [0, 2], [0, 2]] In [45]: [C.next2() for x in range(6)] 7 [0, 3] 8 [0, 4] 9 [1, 1] 10 [1, 2] 11 [1, 3] 12 [1, 4] Out[45]: [[1, 4], [1, 4], [1, 4], [1, 4], [1, 4], [1, 4]] # this should be: [[0,3],[0,4]....[1,4]] or similar
2 个回答
4
我可以稍微改一下这个吗?
def digit(n):
for i in itertools.count():
yield (i%n+1, not i%n)
其实你不需要那个,如果你把整个东西实现成一个简单的迭代器就可以了:
def counter(digits, base):
counter = [0] * digits
def iterator():
for total in itertools.count(1):
for i in range(len(counter)):
counter[i] = (counter[i] + 1) % base
if counter[i]:
break
print total, list(reversed(counter))
yield list(reversed(counter))
return iterator()
c = counter(2, 4)
print list(itertools.islice(c, 10))
如果你想去掉打印(是在调试吧?),可以用一个while循环。
顺便说一下,这样也解决了你最开始的问题,因为 reversed
返回的是列表的一个副本。
哦,对了,现在是从零开始计数的哦;)
15
问题在于,使用 return self.display
时,你返回的是这个列表的一个引用(而不是它的副本)。这样一来,你得到的列表中的每个元素其实都是指向 self.display 的引用。为了更好地理解,看看下面的例子:
>>> a = [1,2]
>>> b = [a,a]
>>> b
[[1, 2], [1, 2]]
>>> a.append(3)
>>> b
[[1, 2, 3], [1, 2, 3]]
你可能想用类似 return self.display[:]
的方式。