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

Как я могу это сделать ? Я не знаю, должен ли я взять кортеж, чтобы отредактировать его или выяснить кортеж indice?

помочь ?

6 70

6 ответов:

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

# 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

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

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

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

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

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

использовать defaultdict:

from collections import defaultdict

urls = defaultdict(int)

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

это всегда работает для меня:

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

чтобы сделать это именно по-вашему? Вы могли бы использовать для...еще структура

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)

несколько вещей, чтобы отметить в этом втором примере:

  • смотрите, как использовать дикт для 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

за исключением первого раза, каждый раз, когда слово видно тест оператора 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