2009-11-07 22 views
60

Ich möchte so etwas tun.Python: Liste von dict, wenn vorhanden inkrementieren einen dict-Wert, wenn nicht ein neues dict anhängen

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

Wie kann ich tun? Ich weiß nicht, ob ich das Tupel nehmen sollte, um es zu bearbeiten oder das Tupel-Indice herauszufinden.

Irgendwelche Hilfe?

+0

Die Antworten sind sehr interessant, danke. – Natim

Antwort

124

Das ist eine sehr seltsame Art, Dinge zu organisieren. Wenn Sie in einem Wörterbuch gespeichert ist, ist dies einfach:

# 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 

Dieser Code für ein Wörterbuch der Zählungen Aktualisierung ein gemeinsames „Muster“ in Python ist. Es ist so verbreitet, dass es eine spezielle Datenstruktur ist, defaultdict, erstellt ihn nur noch einfacher zu machen:

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 

Wenn Sie die defaultdict mit einem Schlüssel zuzugreifen, und der Schlüssel ist nicht bereits in den defaultdict, den Schlüssel wird automatisch mit einem Standardwert hinzugefügt. Die defaultdict nimmt die Callable, die Sie übergeben haben, und ruft sie auf, um den Standardwert zu erhalten. In diesem Fall bestanden wir in Klasse int; Wenn Python int() aufruft, gibt es einen Nullwert zurück. Wenn Sie also zum ersten Mal auf eine URL verweisen, wird deren Anzahl auf Null initialisiert und Sie fügen dann eins zur Anzahl hinzu.

Aber ein Wörterbuch voller zählt ist auch ein gemeinsames Muster, so Python bietet eine ready-to-use-Klasse: containers.Counter Sie erstellen nur eine Counter Instanz durch die Klasse aufrufen, in jedem iterable vorbei; Es erstellt ein Dictionary, in dem die Schlüssel Werte aus dem iterablen Wert sind, und die Werte sind die Anzahl der Male, die der Schlüssel im iterablen Fall angezeigt wurde. Das obige Beispiel wird dann:

from collections import Counter # available in Python 2.7 and newer 

urls_d = Counter(list_of_urls) 

Wenn Sie es wirklich tun müssen, um die Art und Weise Sie zeigten, ist die einfachste und schnellste Weg, eine dieser drei Beispiele zu verwenden wäre, und dann bauen die, die Sie benötigen.

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

Wenn Sie Python verwenden 2.7 oder höher Sie können es in einem Einzeiler tun:

from collections import Counter 

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

Ich mag es, es an eine Django-Vorlage zu senden, damit ich tun kann: '{% für u in URLs%} {{u.url}}: {{u.nbr}} {% endfor%} – Natim

+3

Sie können immer noch do {% für URL, nbr in URLs.items%} {{URL}}: {{nbr}} {% endfor%} – stefanw

+0

Ok hört sich gut an :) Danke – Natim

2

Um es genau so zu machen? Sie könnten die für ... andere Struktur verwenden

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

Aber es ist ziemlich unelegant. Müssen Sie die besuchten URLs wirklich als LISTE speichern?

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) 

Ein paar Dinge in diesem zweiten Beispiel zu beachten: Wenn Sie es als dict, indiziert durch URL-Zeichenfolge, zum Beispiel sortieren, wäre es viel sauberer sein

  • sehen, wie ein mit dict für urls beseitigt die Notwendigkeit, durch die gesamte urls Liste zu gehen, wenn man für eine einzelne url prüft. Dieser Ansatz wird schneller sein.
  • Mit dict() statt Klammern macht den Code kürzer
  • mit list_of_urls, urls und url als Variablennamen des Code ziemlich schwer machen, zu analysieren. Es ist besser, etwas klarer zu finden, wie urls_to_visit, urls_already_visited und current_url. Ich weiß, es ist länger. Aber es ist klarer.

Und natürlich gehe ich davon aus, dass dict(url='http://www.google.fr', nbr=1) eine Vereinfachung der eigenen Datenstruktur ist, anders, weil urls einfach sein könnte:

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

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

, die mit dem sehr elegant defaultdict Haltung bekommen:

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

Die zweite Version ist gut, da ich das Diktat als eine Liste nach konvertieren kann. – Natim

16

Verwendung defaultdict:

from collections import defaultdict 

urls = defaultdict(int) 

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

die Standardwerke verwenden, aber so tut :

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

mit .get können Sie eine Standardrückgabe erhalten, wenn sie nicht existiert. Standardmäßig ist es keine, aber in dem Fall, dass ich euch gesandt hat, wäre es 0.

+5

Eigentlich denke ich, das ist die beste Antwort, da es so ist Agnostiker auf dem gegebenen Wörterbuch, das ein riesiger Bonus imo ist. – Bouncner

+0

Dies ist eine schöne, saubere Lösung. –

+0

Dies sollte die Antwort sein. Effizient, sauber und auf den Punkt !! Ich hoffe stackoverflow erlaubt der Community, die Antwort zusammen mit dem Frage-Poster zu entscheiden. – mowienay

3

dies immer funktioniert gut für mich ...

 

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


 
2

Außer zum ersten Mal, jedes Mal, wenn ein Wort gesehen Der Test der if-Anweisung schlägt fehl. Wenn Sie eine große Anzahl von Wörtern zählen, werden viele wahrscheinlich mehrmals vorkommen. In einer Situation, wo die Initialisierung eines Wert wird nur einmal auftreten werde und die Vermehrung dieses Wertes wird oft auftreten ist es billiger, eine try-Anweisung zu verwenden:

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

Sie mehr darüber lesen können: https://wiki.python.org/moin/PythonSpeed/PerformanceTips