有没有更好的,pythonic方式来做到这一点?

时间:2009-10-20 22:58:28

标签: dictionary set python

这是我的第一个python程序 -

要求:在每行中读取由{adId UserId}组成的文件。对于每个ad​​Id,打印唯一userIds的数量。

这是我的代码,通过阅读python文档放在一起。你能给我一些关于如何用更多python-ish方式写这个的反馈吗?

代码:

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 个答案:

答案 0 :(得分:18)

恭喜,您的代码非常好。 你可以使用一些小技巧来缩短它们的速度。

有一个名为defaultdict的漂亮对象类型,由collections模块提供。您无需检查adDict是否具有adId键,而是可以设置一个默认的dict,其作用类似于常规字典,除非它在没有键时自动为您提供空集()。所以你可以改变

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:

编辑:正如帕克在评论中指出的那样,

for key, value in adDict.iteritems():
如果要同时使用两者,

是迭代dict的最有效方法 循环中的关键和值。在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))

答案 1 :(得分:10)

代码行:

adDict[adId] = set(userId)

不太可能按照您的意愿行事 - 它会将字符串userId视为一系列字母,例如,如果userIdaleax,您将获得四个字符集项目,就像说set(['a', 'l', 'e', 'x'])一样。稍后,当.add(userId) userId aleax再次'aleax'时,会添加第五项,即字符.add,因为set([userId])(与设置初始化程序不同,一个可迭代的参数)将一个项目作为其参数。

要制作包含单个项目的集合,请改用defaultdict

这是一个相当频繁的错误,所以我想清楚地解释一下。话虽如此,setdefault正如其他答案中所建议的那样,显然是正确的方法(避免csv,这从来就不是一个好的设计,也没有良好的表现,也不是很模糊)。

我也会避免{{1}}的有点过分支持在每一行上使用.split和.strip的简单循环......

答案 2 :(得分:7)

您可以将for循环缩短为:

for row in reader:
  adDict.setdefault(row[0], set()).add(row[1])

答案 3 :(得分:3)

而不是:

for row in reader:
    adId = row[0]
    userId = row[1]

使用自动序列解包:

for (adId, userId) in reader:

在:

if ( adId in adDict ):

您不需要括号。

而不是:

if ( adId in adDict ):
    adDict[adId].add(userId)
else:
    adDict[adId] = set(userId)

使用defaultdict

from collections import defaultdict
adDict = defaultDict(set)

# ...

adDict[adId].add(userId)

或者,如果您的教授不允许您使用其他模块,请使用setdefault()

adDict.setdefault(adId, set()).add(userId)

打印时:

for key, value in adDict.items():
    print (key, ',' , len(value))

使用字符串格式可能更容易格式化:

print "%s,%s" % (key, len(value))

或者,如果您使用的是Python 3:

print ("{0},{1}".format (key, len(value)))

答案 4 :(得分:3)

由于你只有一个以空格分隔的文件,我会这样做:

from __future__ import with_statement
from collections import defaultdict

ads = defaultdict(set)
with open("some.csv") as f:
    for ad, user in (line.split(" ") for line in f):
        ads[ad].add(user)

for ad in ads:
    print "%s, %s" % (ad, len(ads[ad]))

答案 5 :(得分:3)

这里有一些很好的答案。

我特别喜欢的一个技巧是让我的代码在将来更容易重用

import csv

def parse_my_file(file_name):
     # some existing code goes here
     return aDict

if __name__ == "__main__":
     #this gets executed if this .py file is run directly, rather than imported
     aDict = parse_my_file("some.csv")
     for key, value in adDict.items():
         print (key, ',' , len(value))

现在,您可以从其他模块导入csv解析器,并获得对aDict的编程访问。

答案 6 :(得分:1)

我所做的唯一改变是一次从阅读器中提取多个元素,并使用字符串格式化打印语句。

import csv

adDict = {}
reader = csv.reader(open("some.csv"), delimiter=' ')
# Can extract multiple elements from a list in the iteration statement:
for adId, userId in reader: 
    if ( adId in adDict ):
        adDict[adId].add(userId)
    else:
        adDict[adId] = set(userId)

for key, value in adDict.items():
    # I believe this gives you more control over how things are formatted:
    print ("%s, %d" % (key, len(value)))

答案 7 :(得分:1)

只是几点点:

用于将行列表提取为变量:

adId, userId = row

if语句不需要大括号:

if adId in adDict:

可以使用异常来处理dict中缺少的Key,但两种方式都运行良好,例如:

try:
    adDict[adId].add(userId)
except KeyError:
    adDict[adId] = set(userId)