有没有更好的、pythonic的做法?
这是我写的第一个Python程序 -
需求:读取一个文件,每一行包含{adId UserId}。对于每个adId,打印出不同的userId数量。
这是我的代码,是我从Python文档中整理出来的。你能给我一些建议,让我可以用更符合Python风格的方式来写吗?
代码:
import csv
adDict = {}
reader = csv.reader(open("some.csv"), delimiter=' ')
for row in reader:
adId = row[0]
userId = row[1]
if ( adId in adDict ):
adDict[adId].add(userId)
else:
adDict[adId] = set(userId)
for key, value in adDict.items():
print (key, ',' , len(value))
谢谢。
8 个回答
你可以把这个for循环简化成这样:
for row in reader:
adDict.setdefault(row[0], set()).add(row[1])
恭喜你,你的代码写得很好!不过有一些小技巧可以让它变得更简洁。
有一个很方便的对象类型叫做 defaultdict,它是 collections 模块提供的。使用这个 defaultdict,你就不需要每次都检查 adDict 里有没有 adId 这个键了。它的工作方式跟普通的字典差不多,但如果你查找的键不存在,它会自动给你一个空的集合 set()。所以你可以把
if ( adId in adDict ):
adDict[adId].add(userId)
else:
adDict[adId] = set(userId)
改成简单的
adDict[adId].add(userId)
另外,
for row in reader:
adId = row[0]
userId = row[1]
你可以把它简化为
for adId,userId in reader:
编辑:正如 Parker 在评论中友好地指出的,
for key, value in adDict.iteritems():
是遍历字典时最有效的方法,特别是当你需要在循环中同时使用键和值的时候。在 Python3 中,你可以使用
for key, value in adDict.items():
因为 items() 会返回一个迭代器。
#!/usr/bin/env python
import csv
from collections import defaultdict
adDict = defaultdict(set)
reader = csv.reader(open("some.csv"), delimiter=' ')
for adId,userId in reader:
adDict[adId].add(userId)
for key,value in adDict.iteritems():
print (key, ',' , len(value))
这段代码:
adDict[adId] = set(userId)
可能不会达到你想要的效果——它会把字符串 userId
当作一串字母来处理。举个例子,如果 userId
是 aleax
,那么你会得到一个包含四个元素的集合,就像 set(['a', 'l', 'e', 'x'])
。之后,如果你再用 userId
作为 aleax
调用 .add(userId)
,它会再添加一个第五个元素,也就是字符串 'aleax'
。这是因为 .add
方法和集合的初始化方式不同,它只接受一个单独的元素。
如果你想创建一个只包含一个元素的集合,应该用 set([userId])
。
这个错误其实挺常见的,所以我想清楚地解释一下。说到这里,其他回答中提到的 defaultdict
显然是个正确的选择(尽量避免使用 setdefault
,这个设计从来就不好,而且性能也不佳,使用起来也比较模糊)。
我还建议不要使用有点复杂的 csv
,而是简单地用一个循环,对每一行使用 .split
和 .strip
。