关于循环:python:dict列表,如果存在,则递增dict值,如果不附加新dict

Python : List of dict, if exists increment a dict value, if not append a new dict

我想做那样的事。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
list_of_urls = ['http://www.google.fr/', 'http://www.google.fr/',
                'http://www.google.cn/', 'http://www.google.com/',
                'http://www.google.fr/', 'http://www.google.fr/',
                'http://www.google.fr/', 'http://www.google.com/',
                'http://www.google.fr/', 'http://www.google.com/',
                'http://www.google.cn/']

urls = [{'url': 'http://www.google.fr/', 'nbr': 1}]

for url in list_of_urls:
    if url in [f['url'] for f in urls]:
         urls[??]['nbr'] += 1
    else:
         urls.append({'url': url, 'nbr': 1})

我该怎么办?我不知道是应该用元组来编辑它,还是计算出元组索引?

有什么帮助吗?


这是一种非常奇怪的组织方式。如果您存储在字典中,这很容易:

1
2
3
4
5
6
7
8
# This example should work in any version of Python.
# urls_d will contain URL keys, with counts as values, like: {'http://www.google.fr/' : 1 }
urls_d = {}
for url in list_of_urls:
    if not url in urls_d:
        urls_d[url] = 1
    else:
        urls_d[url] += 1

更新计数字典的代码是Python中常见的"模式"。这是很常见的一种特殊的数据结构,即defaultdict,它的创建只是为了使这更容易:

1
2
3
4
5
from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

如果您使用密钥访问defaultdict,而该密钥尚未在defaultdict中,则该密钥将自动添加默认值。defaultdict接收您传入的可调用文件,并调用它以获取默认值。在本例中,我们传入了类int;当python调用int()时,它返回一个零值。所以,当您第一次引用一个URL时,它的计数被初始化为零,然后您向该计数中添加一个。

但是一个充满计数的字典也是一种常见的模式,所以python提供了一个随时可用的类:containers.Counter您只需通过调用类来创建一个Counter实例,并传入任何iterable;它构建一个字典,其中键是iterable中的值,值是iterable中键出现的次数。然后上面的例子变成:

1
2
3
from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

如果你真的需要按照你展示的方式来做,最简单和最快的方法就是使用这三个例子中的任何一个,然后构建你需要的一个。

1
2
3
4
5
6
7
from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

urls = [{"url": key,"nbr": value} for key, value in urls_d.items()]

如果您使用的是python 2.7或更高版本,那么您可以在一行程序中完成它:

1
2
3
from collections import Counter

urls = [{"url": key,"nbr": value} for key, value in Counter(list_of_urls).items()]


使用默认值是有效的,但是:

1
urls[url] = urls.get(url, 0) + 1

使用.get时,如果不存在默认返回,则可以获得该返回。默认情况下,它是无的,但在我发送给您的情况下,它将是0。


使用默认dict:

1
2
3
4
5
6
from collections import defaultdict

urls = defaultdict(int)

for url in list_of_urls:
    urls[url] += 1

这对我来说总是很好的:

1
2
3
for url in list_of_urls:
    urls.setdefault(url, 0)
    urls[url] += 1

按你的方式做?您可以使用for…else结构

1
2
3
4
5
6
7
for url in list_of_urls:
    for url_dict in urls:
        if url_dict['url'] == url:
            url_dict['nbr'] += 1
            break
    else:
        urls.append(dict(url=url, nbr=1))

但它很不漂亮。您真的需要将访问过的URL存储为一个列表吗?例如,如果将其排序为按URL字符串编制索引的dict,则会更清晰:

1
2
3
4
5
6
7
urls = {'http://www.google.fr/': dict(url='http://www.google.fr/', nbr=1)}

for url in list_of_urls:
    if url in urls:
        urls[url]['nbr'] += 1
    else:
        urls[url] = dict(url=url, nbr=1)

在第二个例子中需要注意的几点:

  • 请参阅如何使用urls的dict,在测试单个url时,不需要浏览整个urls列表。这种方法会更快。
  • 使用dict( )而不是大括号可以缩短代码长度
  • 使用list_of_urlsurlsurl作为变量名使得代码很难解析。最好找些更清楚的,如urls_to_visiturls_already_visitedcurrent_url。我知道,时间更长。但更清楚了。

当然,我假设dict(url='http://www.google.fr', nbr=1)是对您自己的数据结构的简化,因为否则,urls可能只是:

1
2
3
4
5
6
7
urls = {'http://www.google.fr':1}

for url in list_of_urls:
    if url in urls:
        urls[url] += 1
    else:
        urls[url] = 1

它可以通过默认的dict姿态变得非常优雅:

1
2
3
urls = collections.defaultdict(int)
for url in list_of_urls:
    urls[url] += 1


除了第一次,每次看到单词时,if语句的测试都失败。如果你在数大量的单词,很多单词可能会出现多次。如果一个值的初始化只会发生一次,并且该值的增加会发生多次,那么使用try语句会更便宜:

1
2
3
4
5
6
urls_d = {}
for url in list_of_urls:
    try:
        urls_d[url] += 1
    except KeyError:
        urls_d[url] = 1

您可以阅读以下内容:https://wiki.python.org/moin/pythonspeed/performancetips