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
I believe I'm getting bitten by some combination of nested scoping rules and list comprehensions. Jeremy Hylton's blog post is suggestive about the causes, but I don't really understand CPython's implementation well-enough to figure out how to get around this.
Here is an (overcomplicated?) example. If people have a simpler one that demos it, I'd like to hear it. The issue: the list comprehensions using next() are filled with the result from the last iteration.
edit: The Problem:
What exactly is going on with this, and how do I fix this? Do I have to use a standard for loop? Clearly the function is running the correct number of times, but the list comprehensions end up with the final value instead of the result of each loop.
Some hypotheses:
- generators?
- lazy filling of list comprehensions?
code
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)]
OUTPUT
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题是,使用
return self.display
您将返回对此列表的引用(而不是副本)。 所以你最终得到的是一个列表,其中每个元素都是对 self.display 的引用。 为了说明这一点,请看以下内容:您可能想要使用类似
return self.display[:]
的内容。The problem is that with
return self.display
you return a reference to this list (not a copy). So what you end up with is a list where each element is a reference to self.display. To illustrate, look at the following:You probably want to use something like
return self.display[:]
.介意我重构一下吗?
但实际上你不需要那个,如果你将整个事情实现为一个简单的迭代器:
如果你想摆脱打印(调试,是吗?),请使用 while 循环。
顺便说一句,这也解决了您最初的问题,因为
reversed
返回列表的副本。哦,现在是零基础了;)
Mind if i refactor this a bit?
But actually you don't need that one, if you implement the whole thing as a simple iterator:
If you want to get rid of the print (debugging, is it?), go with a while-loop.
This incindentally also solves your initial problem, because
reversed
returns a copy of the list.Oh, and it's zero-based now ;)