Python: список dict, если существует, увеличивает значение dict, если не добавляет новый dict

Я хотел бы сделать что-то подобное.

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

Это очень странный способ организовать вещи. Если вы храните в словаре, это легко:

# 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

Этот код для обновления словаря counts является общим "шаблоном" в Python. Общеизвестно, что существует специальная структура данных, defaultdict, созданная только для того, чтобы сделать это еще проще:

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, вызывая класс, передавая его в любом итерабельном; он создает словарь, где ключи являются значениями из итерируемого, а значения - количеством того, сколько раз ключ появился в истребителе. Приведенный выше пример будет следующим:

from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

Если вам действительно нужно сделать это так, как вы показали, самым простым и быстрым способом было бы использовать любой из этих трех примеров, а затем построить тот, который вам нужен.

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 или новее, вы можете сделать это в одном слое:

from collections import Counter

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

Ответ 2

Использование работы по умолчанию, но так же:

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

используя .get, вы можете получить возврат по умолчанию, если он не существует. По умолчанию это None, но в том случае, когда я отправил вас, это будет 0.

Ответ 3

Используйте defaultdict:

from collections import defaultdict

urls = defaultdict(int)

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

Ответ 4

это всегда отлично работает для меня...


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



Ответ 5

Сделать это именно так? Вы можете использовать структуру for... 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))

Но это довольно неэлегантно. Вам действительно нужно хранить посещаемые URL как СПИСОК? Если вы отсортируете его как dict, проиндексированный по строке URL, например, это будет более чистым:

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)

Несколько вещей, которые следует отметить в этом втором примере:

  • посмотрите, как использование dict для urls устраняет необходимость прохождения через весь список urls при тестировании для одного url. Этот подход будет быстрее.
  • Использование dict( ) вместо фигурных скобок делает ваш код короче
  • используя list_of_urls, urls и url, поскольку имена переменных делают код довольно сложным для синтаксического анализа. Лучше найти что-то более четкое, например urls_to_visit, urls_already_visited и current_url. Я знаю, это дольше. Но это яснее.

И, конечно, я предполагаю, что dict(url='http://www.google.fr', nbr=1) является упрощением вашей собственной структуры данных, потому что в противном случае urls может просто быть:

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

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

Что может стать очень элегантным с defaultdict позиция:

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

Ответ 6

За исключением первого раза, каждый раз, когда отображается слово, проверка выполнения if не выполняется. Если вы считаете большое количество слов, многие, вероятно, будут встречаться несколько раз. В ситуации, когда инициализация значения происходит только один раз, а увеличение этого значения произойдет много раз, более дешево использовать оператор try:

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