Python: Danh sách dict, nếu tồn tại thì tăng một giá trị dict, nếu không thêm một dict mới


107

Tôi muốn làm một cái gì đó như thế.

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

Làm thế nào tôi có thể làm được ? Tôi không biết mình có nên lấy tuple để chỉnh sửa nó hay tìm ra các chỉ số của tuple không?

Bất kỳ giúp đỡ?

Câu trả lời:


207

Đó là một cách rất lạ để tổ chức mọi thứ. Nếu bạn lưu trữ trong từ điển, điều này rất dễ dàng:

# 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

Mã này để cập nhật từ điển số đếm là một "mẫu" phổ biến trong Python. Nó phổ biến đến mức có một cấu trúc dữ liệu đặc biệt defaultdict, được tạo ra chỉ để làm cho việc này trở nên dễ dàng hơn:

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

Nếu bạn truy cập vào khóa defaultdictbằng cách sử dụng một khóa và khóa chưa có trong khóa defaultdict, khóa sẽ tự động được thêm vào với một giá trị mặc định. Hàm defaultdictlấy giá trị có thể gọi mà bạn đã chuyển vào và gọi nó để nhận giá trị mặc định. Trong trường hợp này, chúng tôi đã vượt qua trong lớp int; khi Python gọi int()nó sẽ trả về giá trị 0. Vì vậy, lần đầu tiên bạn tham chiếu đến một URL, số lượng của nó được khởi tạo bằng 0 và sau đó bạn thêm một URL vào số lượng.

Nhưng một từ điển đầy số đếm cũng là một mẫu phổ biến, vì vậy Python cung cấp một lớp sẵn sàng để sử dụng: containers.Counter Bạn chỉ cần tạo một Counterthể hiện bằng cách gọi lớp, truyền vào bất kỳ lớp nào có thể lặp lại; nó xây dựng một từ điển trong đó các khóa là giá trị từ có thể lặp lại và các giá trị là số lần khóa xuất hiện trong có thể lặp lại. Ví dụ trên sau đó trở thành:

from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

Nếu bạn thực sự cần làm theo cách bạn đã trình bày, cách dễ nhất và nhanh nhất là sử dụng bất kỳ một trong ba ví dụ này, sau đó xây dựng một ví dụ bạn cần.

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

Nếu bạn đang sử dụng Python 2.7 hoặc mới hơn, bạn có thể làm điều đó trong một lớp lót:

from collections import Counter

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

Tôi làm như vậy để gửi nó đến một mẫu django để tôi có thể thực hiện: `` {% for u in urls%} {{u.url}}: {{u.nbr}} {% endfor%}
Natim

3
Bạn vẫn có thể thực hiện {% cho url, nbr trong urls.items%} {{url}}: {{nbr}} {% endfor%}
stefanw

160

Sử dụng mặc định hoạt động, nhưng cũng vậy:

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

bằng cách sử dụng .get, bạn có thể nhận được lợi nhuận mặc định nếu nó không tồn tại. Theo mặc định, nó là Không, nhưng trong trường hợp tôi gửi cho bạn, nó sẽ là 0.


12
Trên thực tế, tôi nghĩ đây là câu trả lời tốt nhất, vì nó là bất khả tri trên từ điển đã cho, đó là một imo tiền thưởng rất lớn.
Bouncner

Đây là một giải pháp sạch đẹp.
Dylan Hogg

1
Đây nên là câu trả lời. Hiệu quả, sạch sẽ và phù hợp !! Tôi hy vọng stackoverflow cho phép cộng đồng quyết định câu trả lời cùng với người đăng câu hỏi.
mowienay

Thực sự thích câu trả lời này chỉ không hoạt động nếu khóa là Không ^^ Hoặc tốt ... Cần thêm một số bước ...
Cedric


17

Điều này luôn hoạt động tốt đối với tôi:

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

3

Để làm điều đó chính xác theo cách của bạn? Bạn có thể sử dụng cấu trúc 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))

Nhưng nó khá không nhã nhặn. Bạn có thực sự phải lưu trữ các url đã truy cập dưới dạng DANH SÁCH không? Ví dụ: nếu bạn sắp xếp nó dưới dạng dict, được lập chỉ mục bằng chuỗi url, nó sẽ gọn gàng hơn:

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)

Một số điều cần lưu ý trong ví dụ thứ hai đó:

  • xem cách sử dụng dict cho urlsloại bỏ nhu cầu xem qua toàn bộ urlsdanh sách khi kiểm tra một đơn lẻ url. Cách làm này sẽ nhanh hơn.
  • Sử dụng dict( ) thay vì dấu ngoặc nhọn làm cho mã của bạn ngắn hơn
  • bằng cách sử dụng list_of_urls, urlsurldưới dạng tên biến làm cho mã khá khó phân tích cú pháp. Đó là tốt hơn để tìm một cái gì đó rõ ràng hơn, chẳng hạn như urls_to_visit, urls_already_visitedcurrent_url. Tôi biết, nó còn lâu hơn. Nhưng nó rõ ràng hơn.

Và tất nhiên, tôi giả định rằng đó dict(url='http://www.google.fr', nbr=1)là sự đơn giản hóa cấu trúc dữ liệu của riêng bạn, bởi vì nếu không, urlscó thể đơn giản là:

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

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

Mà có thể nhận được rất thanh lịch với defaultdict lập trường:

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

Phiên bản thứ hai là tốt vì tôi có thể chuyển đổi dict thành một danh sách sau đó.
Natim

3

Ngoại trừ lần đầu tiên, mỗi lần một từ được nhìn thấy, bài kiểm tra của câu lệnh if không thành công. Nếu bạn đang đếm một số lượng lớn các từ, nhiều từ có thể xảy ra nhiều lần. Trong tình huống mà việc khởi tạo một giá trị chỉ diễn ra một lần và việc tăng giá trị đó sẽ diễn ra nhiều lần, thì việc sử dụng câu lệnh try sẽ rẻ hơn:

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

bạn có thể đọc thêm về điều này: https://wiki.python.org/moin/PythonSpeed/PerformanceTips

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.