web-dev-qa-db-fra.com

Python: Liste des dict, s'il existe, incrémente une valeur dict, sinon ajoute un nouveau dict

Je voudrais faire quelque chose comme ça.

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})

Comment puis-je faire ? Je ne sais pas si je devrais prendre le Tuple pour le modifier ou calculer les indices de Tuple?

De l'aide ?

84
Natim

C'est une façon très étrange d'organiser les choses. Si vous avez stocké dans un dictionnaire, c'est facile:

# 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

Ce code permettant de mettre à jour un dictionnaire de comptes est un "motif" commun en Python. Il est si courant qu’il existe une structure de données spéciale, defaultdict, créée simplement pour rendre cela encore plus facile:

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

Si vous accédez au defaultdict à l'aide d'une clé et que la clé ne figure pas déjà dans le defaultdict, la clé est automatiquement ajoutée avec une valeur par défaut. defaultdict prend l'appelable que vous avez transmis et l'appelle pour obtenir la valeur par défaut. Dans ce cas, nous avons passé dans la classe int; when Python appelle int()] renvoie une valeur de zéro. Ainsi, la première fois que vous référencez une URL, son nombre est initialisé à zéro, puis vous en ajoutez un. .

Mais un dictionnaire plein de comptes est également un modèle courant, donc Python fournit une classe prête à l'emploi: containers.Counter Vous créez simplement une instance Counter en appeler la classe, en passant n'importe quelle valeur itérable; il crée un dictionnaire dans lequel les clés sont des valeurs de la valeur itérable, et les valeurs représentent le nombre de fois où la clé est apparue dans la valeur itérable. L'exemple ci-dessus devient alors:

from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

Si vous avez vraiment besoin de le faire comme vous l'avez montré, le moyen le plus simple et le plus rapide consiste à utiliser l'un quelconque de ces trois exemples, puis de créer celui dont vous avez besoin.

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()]

Si vous utilisez Python 2.7 ou plus récent, vous pouvez le faire en une ligne:

from collections import Counter

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

L'utilisation de la valeur par défaut fonctionne, mais il en va de même:

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

en utilisant .get, vous pouvez obtenir un retour par défaut s'il n'existe pas. Par défaut c'est Aucun, mais dans le cas où je vous ai envoyé, ce serait 0.

122
mikelikespie

Utilisez defaultdict :

from collections import defaultdict

urls = defaultdict(int)

for url in list_of_urls:
    urls[url] += 1
19
Greg Hewgill

Cela fonctionne toujours bien pour moi:

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

Le faire exactement à votre façon? Vous pouvez utiliser la structure de ... else

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))

Mais c'est assez inélégant. Devez-vous vraiment stocker les URL visitées sous forme de LISTE? Si vous le triez comme un dict, indexé par une chaîne d'URL, par exemple, ce serait bien plus propre:

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)

Quelques points à noter dans ce deuxième exemple:

  • voyez comment l'utilisation d'un dict pour urls supprime la nécessité de parcourir toute la liste urls lors du test d'un seul url. Cette approche sera plus rapide.
  • L'utilisation de dict( ) au lieu d'accolades rend votre code plus court
  • utiliser list_of_urls, urls et url comme noms de variables rendent le code assez difficile à analyser. Il est préférable de trouver quelque chose de plus clair, tel que urls_to_visit, urls_already_visited Et current_url. Je sais, c'est plus long. Mais c'est plus clair.

Et bien sûr, je suppose que dict(url='http://www.google.fr', nbr=1) est une simplification de votre propre structure de données, car sinon, urls pourrait simplement être:

urls = {'http://www.google.fr':1}

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

Ce qui peut être très élégant avec la position defaultdict :

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

À l'exception de la première fois, chaque fois qu'un mot est vu, le test de l'instruction if échoue. Si vous comptez un grand nombre de mots, ceux-ci seront probablement répétés plusieurs fois. Dans une situation où l'initialisation d'une valeur ne se produira qu'une seule fois et que l'augmentation de cette valeur se produira plusieurs fois, il est moins coûteux d'utiliser une instruction try:

urls_d = {}
for url in list_of_urls:
    try:
        urls_d[url] += 1
    except KeyError:
        urls_d[url] = 1

vous pouvez en savoir plus à ce sujet: https://wiki.python.org/moin/PythonSpeed/PerformanceTips

2
pilatipus