2009-11-07 6 views
60

Chciałbym zrobić coś takiego.Python: Lista dictów, jeśli istnieje wartość incydentu, jeśli nie dodasz nowego dicta

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

Jak mogę to zrobić? Nie wiem, czy powinienem wziąć krotkę, żeby ją edytować, czy wymyślić indeks krotki?

Każda pomoc?

+0

Odpowiedzi są bardzo interesujące, dziękuję. – Natim

Odpowiedz

124

To bardzo dziwny sposób organizowania rzeczy. Jeśli przechowywane w słowniku, to jest proste:

# 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 

Ten kod do aktualizacji słownika liczy jest częstym „wzór” w Pythonie. To jest tak powszechne, że istnieje specjalna struktura danych, defaultdict, stworzony tylko do tego jeszcze łatwiejszy:

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 

Jeśli dostęp do defaultdict użyciu klucza i klucz nie jest już w defaultdict, klucz jest automatycznie dodawany z wartością domyślną. Numer defaultdict przejmuje żądaną liczbę wywołań i wywołuje ją, aby uzyskać wartość domyślną. W tym przypadku przeszliśmy w klasie int; gdy Python wywołuje int(), zwraca wartość zero. Przy pierwszym odwołaniu się do adresu URL jego licznik jest inicjowany do zera, a następnie do licznika.

Ale słownik pełen zliczeń jest również powszechnym wzorcem, więc Python zapewnia klasę gotową do użycia: containers.Counter Po prostu tworzysz instancję Counter, wywołując klasę, przekazując w dowolnej iteracji; buduje słownik, w którym klucze są wartościami z iteracji, a wartości są zliczeniami ile razy klucz pojawił się w iteracji. Powyższy przykład wówczas postać:

from collections import Counter # available in Python 2.7 and newer 

urls_d = Counter(list_of_urls) 

Jeśli naprawdę trzeba to zrobić tak, jak pokazał, najprostszym i najszybszym sposobem byłoby wykorzystanie jednego z tych trzech przykładów, a następnie zbudować jedną, czego potrzebujesz.

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

Jeśli używasz Pythona 2.7 lub nowszej można to zrobić w jednej wkładki:

from collections import Counter 

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

Chcę to wysłać do szablonu django, więc mogę to zrobić: '{% dla Ciebie w adresach URL%} {{u.url}}: {{u.nbr}} {% endfor%} – Natim

+3

Nadal możesz do {% dla adresu URL, numeru w urls.items%} {{url}}: {{nbr}} {% endfor%} – stefanw

+0

OK brzmi świetnie :) Dziękuję – Natim

2

Aby zrobić to dokładnie na swój sposób? Można użyć ... dla innej struktury:

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

Ale jest dość nieelegancka. Czy naprawdę musisz przechowywać odwiedzane adresy URL jako LISTĘ? Jeśli posortować je jako dict, indeksowane przez ciąg URL, na przykład, byłoby sposobem czystsze:

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) 

Kilka rzeczy do odnotowania w tym drugim przykładzie:

  • zobaczyć, jak za pomocą dict dla urls usuwa potrzebę przeszukiwania całej listy urls podczas testowania pojedynczego url. Takie podejście będzie szybsze.
  • Korzystanie dict() zamiast szelek sprawia, że ​​kod krótszy
  • użyciu list_of_urls, urls i url jako nazwy zmiennych, aby kod dość trudne do analizowania. Lepiej znaleźć coś bardziej przejrzystego, na przykład urls_to_visit, urls_already_visited i . Wiem, że jest dłużej. Ale jest jaśniejsze.

I oczywiście jestem przy założeniu, że dict(url='http://www.google.fr', nbr=1) jest uproszczeniem własnej struktury danych, ponieważ w przeciwnym razie, urls może być po prostu:

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

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

Które można uzyskać bardzo elegancki z defaultdict stanowisko:

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

Druga wersja jest dobra, ponieważ mogę przekonwertować dykt jako listę po. – Natim

16

użytkowania defaultdict:

from collections import defaultdict 

urls = defaultdict(int) 

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

Korzystanie prace domyślne, ale tak robi :

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

za pomocą .get, możesz uzyskać domyślny zwrot, jeśli nie istnieje. Domyślnie jest to brak, ale w przypadku Wysłałem ci, byłoby 0.

+5

Właściwie uważam, że jest to najlepsza odpowiedź, ponieważ jest agnostyczny w danym słowniku, który jest ogromnym bonusowym imo. – Bouncner

+0

To jest ładne, czyste rozwiązanie. –

+0

To powinna być odpowiedź. Wydajne, czyste i na temat !! Mam nadzieję, że stackoverflow pozwoli społeczności zdecydować odpowiedź wraz z plakatem pytanie. – mowienay

3

to zawsze działa dobrze dla mnie ...

 

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


 
2

wyjątkiem po raz pierwszy, za każdym razem słowo jest postrzegane test instrukcji if nie powiódł się. Jeśli liczysz dużą liczbę słów, wiele z nich prawdopodobnie pojawi się wiele razy. W sytuacji, gdy inicjalizacja wartości tylko będzie występować tylko raz i zwiększeniem tej wartości nastąpi wiele razy taniej jest użyć instrukcji try:

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

można przeczytać więcej na ten temat: https://wiki.python.org/moin/PythonSpeed/PerformanceTips

Powiązane problemy