Python: Varsa dikte listesi, yeni bir dikte eklemezse bir dikt değerini artırır


107

Böyle bir şey yapmak isterim.

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

Nasıl yapabilirim ? Tuple'ı düzenlemek veya tuple indekslerini bulmak için almalı mıyım bilmiyorum?

Herhangi bir yardım ?

Yanıtlar:


207

Bir şeyleri organize etmenin çok garip bir yolu bu. Bir sözlükte sakladıysanız, bu kolaydır:

# 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

Bir sayı sözlüğünü güncellemek için kullanılan bu kod, Python'da yaygın bir "kalıp" tır. O kadar yaygındır ki, defaultdictbunu daha da kolaylaştırmak için oluşturulmuş özel bir veri yapısı vardır :

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

defaultdictBir anahtar kullanarak erişirseniz ve anahtar zaten içinde değilse defaultdict, anahtar otomatik olarak varsayılan bir değerle eklenir. , Girdiğiniz defaultdictçağrılabilir'i alır ve varsayılan değeri almak için çağırır. Bu durumda sınıfta geçtik int; Python çağırdığında int()sıfır değeri döndürür. Bu nedenle, bir URL'ye ilk başvurduğunuzda, sayısı sıfır olarak başlatılır ve ardından sayıma bir tane eklersiniz.

Ancak sayımlarla dolu bir sözlük de yaygın bir kalıptır, bu nedenle Python kullanıma hazır bir sınıf sağlar: containers.Counter Sadece Countersınıfı çağırarak, herhangi bir yinelenebilirliği ileterek bir örnek oluşturursunuz ; anahtarların yinelenebilir değerlerden olduğu ve değerlerin anahtarın yinelenebilir dosyada kaç kez göründüğünün sayıları olduğu bir sözlük oluşturur. Yukarıdaki örnek şu hale gelir:

from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

Gösterdiğiniz gibi yapmanız gerekiyorsa, en kolay ve en hızlı yol bu üç örnekten herhangi birini kullanmak ve ardından ihtiyacınız olanı oluşturmaktır.

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 veya daha yenisini kullanıyorsanız, bunu tek satırda yapabilirsiniz:

from collections import Counter

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

Bunu bir django şablonuna göndermeyi seviyorum, böylece şunları yapabilirim: "{% for u in urls%} {{u.url}}: {{u.nbr}} {% endfor%}
Natim

3
Yine de {% url için, urls.items içinde nbr yapabilirsiniz {{url}}: {{nbr}} {% endfor%}
stefanw

161

Varsayılanı kullanmak işe yarasa da, şu şekilde:

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

kullanarak .get, mevcut değilse varsayılan bir getiri elde edebilirsiniz. Varsayılan olarak Yoktur, ancak size gönderdiğim durumda 0 olur.


13
Aslında, verilen sözlükte agnostik olduğu için en iyi yanıtın bu olduğunu düşünüyorum, bu büyük bir bonus imo.
Bouncner

Bu güzel ve temiz bir çözüm.
Dylan Hogg

2
Cevap bu olmalı. Etkili, temiz ve isabetli !! Umarım stackoverflow, topluluğun soru posteriyle birlikte yanıta karar vermesine olanak tanır.
mowienay

Gerçekten bu yanıt, anahtar Yok ise işe yaramıyor ^^ Ya da pekala ... Biraz daha adım gerekiyor ...
Cedric


17

Bu benim için her zaman iyi çalışıyor:

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

mikelikespie'nin çözümünün açıklaması
thanos.a

3

Bunu tam olarak kendi yönteminizle yapmak için? For ... else yapısını kullanabilirsiniz

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

Ama oldukça uygunsuz. Ziyaret edilen url'leri LİSTE olarak saklamanız gerekiyor mu? Örneğin url dizesine göre dizine alınmış bir dikte olarak sıralarsanız, çok daha temiz olur:

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)

Bu ikinci örnekte dikkat edilmesi gereken birkaç nokta:

  • Tek bir tek için test yaparken urlstüm urlslisteyi gözden geçirme ihtiyacını nasıl ortadan kaldıracağını görün url. Bu yaklaşım daha hızlı olacak.
  • Parantez dict( )yerine kullanmak kodunuzu kısaltır
  • kullanılarak list_of_urls, urlsve urldeğişken adları ayrıştırmak için kod oldukça zor yaptıkça. Bu gibi net bir şey bulmak daha iyidir urls_to_visit, urls_already_visitedve current_url. Biliyorum, daha uzun. Ama daha net.

Ve tabii ki bunun dict(url='http://www.google.fr', nbr=1)kendi veri yapınızın basitleştirilmesi olduğunu varsayıyorum , çünkü aksi takdirde urlsbasitçe şunlar olabilir:

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

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

Varsayılan dikt duruşuyla çok şıklaşabilir :

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

İkinci versiyon, dikteyi daha sonra bir liste olarak dönüştürebildiğim için iyidir.
Natim

3

İlk sefer dışında, her kelime görüldüğünde if ifadesinin testi başarısız olur. Çok sayıda kelime sayıyorsanız, çoğu muhtemelen birden çok kez geçecektir. Bir değerin başlatılmasının yalnızca bir kez gerçekleşeceği ve bu değerin artırılmasının birçok kez gerçekleşeceği bir durumda, bir try ifadesini kullanmak daha ucuzdur:

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

bununla ilgili daha fazla bilgi edinebilirsiniz: https://wiki.python.org/moin/PythonSpeed/PerformanceTips

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.