Python:使用for循环进行索引赋值

4

在阅读Toby Segaran所著的《集体智慧编程》时,我遇到了一些索引分配技巧,对此并不是很熟悉。

例如:

createkey='_'.join(sorted([str(wi) for wi in wordids]))

或者:

normalizedscores = dict([(u,float(l)/maxscore) for (u,l) in linkscores.items()])

所有嵌套元组的索引让我有点困惑。实际上,这些变量被分配了什么?我认为显然.join会输出一个字符串,但后者呢?如果有人能解释一下这些循环的机制,我会非常感激。我想这些是相当常见的技巧,但作为 Python 的新手,问一下可能有些羞耻。谢谢!


@agf,你能给个例子吗?我对这种方法还不熟悉。谢谢! - DeaconDesperado
sorted() 无论如何都会构建一个列表吗? - Tim Pietzcker
@agf:我刚刚计时了一下,使用生成器表达式进行排序()比使用列表要慢大约10%。 - Tim Pietzcker
@TimPietzcker 但是使用列表推导式,列表需要存在两次,而不是一次。那是哪个版本的?列表推导式基本上是在Python 3.2+上对生成器表达式进行list()操作,但在早期版本中可能会有10%的差异,这一点也不奇怪。 - agf
@agf:不,它不必存在两次。它是在列表推导期间构建的,并作为参数传递给sorted()(然后返回一个新列表)。对于genex,sorted()首先必须从genex结果构建一个列表,然后构造一个它返回的列表。我在Python 3.2.2上计时过,效果比Python 2.7.2更明显(我已在我的新编辑答案中包含了计时)。 - Tim Pietzcker
显示剩余2条评论
6个回答

15
[str(wi) for wi in wordids]

是一个列表推导式

a = [str(wi) for wi in wordids]

是相同的意思

a = []
for wi in wordids:
    a.append(str(wi))

所以
createkey='_'.join(sorted([str(wi) for wi in wordids]))

wordids中的每个项目创建一个字符串列表,然后对该列表进行排序,并使用_作为分隔符将其连接成一个大字符串。
正如agf所指出的那样,您还可以使用生成器表达式,它看起来就像列表推导式,但是用圆括号代替方括号。如果您以后不需要它(除了迭代它),则可以避免构建列表。如果您已经有了括号,例如在此处与sorted(...)一起使用,则可以直接删除方括号。
然而,在这种特殊情况下,您不会获得性能优势(实际上,它会慢大约10%;我计时了),因为sorted()无论如何都需要构建一个列表,但它看起来更好一些:
createkey='_'.join(sorted(str(wi) for wi in wordids))

normalizedscores = dict([(u,float(l)/maxscore) for (u,l) in linkscores.items()])

这段代码迭代遍历字典 linkscores 的每一个键值对,创建一个键/l/maxscore 元组列表, 然后将该列表转换回字典。

然而,自从Python 2.7版本起,你也可以使用字典推导式

normalizedscores = {u:float(l)/maxscore for (u,l) in linkscores.items()}

这里是一些时间数据:
Python 3.2.2
>>> import timeit
>>> timeit.timeit(stmt="a = '_'.join(sorted([str(x) for x in n]))", setup="import random; n = [random.randint(0,1000) for i in range(100)]")
61.37724242267409
>>> timeit.timeit(stmt="a = '_'.join(sorted(str(x) for x in n))", setup="import random; n = [random.randint(0,1000) for i in range(100)]")
66.01814811313774

Python 2.7.2

>>> import timeit
>>> timeit.timeit(stmt="a = '_'.join(sorted([str(x) for x in n]))", setup="import random; n = [random.randint(0,1000) for i in range(100)]")
58.01728623923137
>>> timeit.timeit(stmt="a = '_'.join(sorted(str(x) for x in n))", setup="import random; n = [random.randint(0,1000) for i in range(100)]")
60.58927580777687

@andronikus 不是。Python 2.7。 - agf
我认为作者在写作时可能使用了较旧版本的Python,因为我从他的博客下载了修订后的示例代码,在那里他使用了你描述的字典推导方法。 - DeaconDesperado
哦,我会诅咒的:http://docs.python.org/dev/whatsnew/2.7.html#python-3-1-features。反对意见已撤回! - andronikus
@andronikus: 我已经让我的回答更加清晰了,谢谢。毕竟,Python 2.5和2.6仍然广泛使用… - Tim Pietzcker
是的,我想我仍然在使用其中之一。直到现在我从未听说过字典推导,这并没有帮助。非常酷! - andronikus

3

让我们看一下第一个:

  1. str(wi) for wi in wordidswordids中的每个元素转换为字符串。
  2. sorted(...) 对它们进行排序(按字典顺序)。
  3. '_'.join(...) 将排序后的单词ID合并成一个带有下划线的字符串。

现在来看第二个:

normalizedscores = dict([(u,float(1)/maxscore) for (u,l) in linkscores.items()])
  1. linkscores 是一个字典(或类字典的对象)。
  2. for (u,l) in linkscores.items() 迭代遍历字典中所有条目,对于每个条目将键和值分配给 ul
  3. (u,float(1)/maxscore) 是一个元组,其中第一个元素是 u,第二个元素是 1/maxscore(在我看来,这可能是一个拼写错误:float(l)/maxscore 更有意义--请注意小写字母el代替数字一)。
  4. dict(...) 从元组列表构建一个字典,其中每个元组的第一个元素被视为键,第二个元素被视为值。

简而言之,它复制了字典,保留键并将每个值除以 maxscore


如果您解释一下列表推导式的基本作用(我感觉OP可能不知道,但我可能错了),则加2分。 - naeg

1

后者相当于:

normalizedscores = {}
for u, l in linkscores.items():
    normalizedscores[u] = float(l) / maxscore

1
[(u,float(1)/maxscore) for (u,l) in linkscores.items()]

这将通过迭代linkscores.items()中的元组并为每个元组计算(u, float(l)/maxscore)来创建一个列表。

dict([this list])

使用列表推导式从linkscores中的每个项目创建一个包含条目(u, float(l)/maxscore)dict

另一个从元组列表创建字典的例子:

>>> l = [(1,2), (3,4), (5,6)]
>>> d = dict(l)
>>> d
{1: 2, 3: 4, 5: 6}

1
这是一个关于编程的例子...第一个例子。
>>> wordids = [1,2,4,3,10,7]
>>> createkey='_'.join(sorted([str(wi) for wi in wordids]))
>>> print createkey
1_10_2_3_4_7

它正在使用for循环迭代列表,对列表进行排序,然后将所有排序后的值连接成一个字符串,用下划线分隔值。

1

方括号[]内看起来奇怪的业务被称为列表推导式,它基本上是一种非常简洁的构建列表的方式。myList = [str(wi) for wi in wordids]等同于:

myList = []

for wi in wordids:
  myList.append(str(wi))

sorted()函数对列表进行排序,join()函数将这些列表项用下划线分隔组成字符串,例如:item1_item2_item3_...

第二个任务更加复杂/简洁,但是以下是其工作原理:

  • linkscores看起来像一个字典,items()方法从该字典返回一个(key, value)元组的列表。因此,for (u,l) in linkscores.items()循环遍历该列表。
  • 对于这些元组中的每一个,我们创建一个新的元组,其中包含(u, float(l)/maxscore),并将其添加到列表中。因此,这一步基本上将您的(item, value)列表更改为(item, normalized value)元组的列表。
  • dict()函数将其转换回字典。

这样做的总体结果是将字典中的所有值进行归一化处理。可能有更简单/冗长的方法来实现这一点,但这种方式的好处在于看起来很酷。我不喜欢使用列表推导式来进行疯狂的操作,因为这会影响可读性,所以如果你不想自己编写这种代码,完全没有关系!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接