我目前正在审查this post关于Python中的数独求解器的信息。我试图逐行解构它,并且经历了以下
digits = '123456789'
rows = 'ABCDEFGHI'
cols = digits
#squares will give you all the squares of a Sudoku, from A1-A9 to I1-I9
squares = cross(rows, cols)
unitlist = ([cross(rows, c) for c in cols] +
[cross(r, cols) for r in rows] +
[cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')])
#each square has 3 units : the row it is in, the column it is in, and the "block" of 3x3 squares it is in :
units = dict((s, [u for u in unitlist if s in u])
for s in squares)
最后一部分让我感到震惊。如果我没记错的话,我们在生成器内部有一个列表理解。 但是,在dict函数内部执行的“ for s in squares”是什么? 为什么我不能写出以下内容并获得相同的结果?
for s in squares :
units3 = dict((s, [u for u in unitlist if s in u]))
这对我来说似乎很确定,不幸的是,我为此目的寻找资源的所有尝试最终都得到指向dict创建基础教程或dict值循环的结果。
您能否告诉我dict函数内部的for循环是如何工作的,或者让我针对该主题找到一个好的教程?
答案 0 :(得分:3)
这不是列表推导,而是生成器表达式。两者是相关的,但没有构建列表。生成器表达式生成一个生成器对象,该对象可以延迟进行迭代,并在循环表达式的每个步骤中执行。
例如,您可以创建一个这样的表达式来计算平方:
>>> squares = (i ** 2 for i in range(10))
>>> squares
<generator object <genexpr> at 0x10c832468>
>>> next(squares)
0
>>> next(squares)
1
>>> next(squares)
4
next()
函数使迭代器前进以产生下一个值。在这之间,生成器被暂停,剩余值尚未计算。
在您发现的示例中,生成器表达式是dict()
调用的唯一参数,在这种情况下,可以忽略生成器表达式的(...)
。您还可以编写dict((...))
,它会产生完全相同的结果;如果调用包含多个参数,则需要使用这些括号。列表推导嵌套在dict(...)
生成器表达式中。
生成器生成(key, value)
元组,dict()
可调用的元组用于创建字典。参见dict()
documentation:
[...] 否则,位置参数必须是可迭代的对象。可迭代对象中的每个项目本身都必须是具有两个对象的可迭代对象。每个项目的第一个对象成为新字典中的键,第二个对象成为相应的值。
所以等效的for
循环语句将是:
units = {}
for s in squares:
units[s] = [u for u in unitlist if s in u]
请注意[u for u in unitlist if s in u]
列表理解;它是一个独立于生成器表达式的独立表达式(但每次都会使用s
的当前值)。该循环在我们必须首先在此处创建的字典中设置了一个值,但否则,每个可迭代的步骤都具有相同的结果:设置了键s
,并将列表理解结果作为该值。
在Python 2.7和Python 3中,您可以使用 dictionary comprehension 而不是生成器表达式来生成完全相同的字典:
units = {s: [u for u in unitlist if s in u] for s in squares}
字典理解的模式是{<key expression>: <value expression> for ... in ... <optionally more if filters and for loops>}
;将其与您找到的代码使用的dict((<key expression>, <value expression>) for ... in ... <optionally more if filters and for loops>)
模式进行比较。字典理解速度更快,因为解释器不再需要担心如何在每个步骤中停止和启动生成器表达式,也不必定位dict
名称并对其进行调出。