Kiểm tra xem một khóa đã cho có tồn tại trong từ điển không và tăng nó


294

Đưa ra một từ điển, làm thế nào tôi có thể tìm ra nếu một khóa nhất định trong từ điển đó đã được đặt thành giá trị không phải là Không?

Tức là tôi muốn làm điều này:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

Tức là tôi muốn tăng giá trị nếu đã có một cái ở đó hoặc đặt nó thành 1 nếu không.


11
Mã nit nhỏ: mã đặt my_dict [key] thành 1 nếu đã có thứ gì đó ở đó và tăng nó nếu không có. Tôi nghĩ bạn muốn ==, không phải! =.
QuantumFool

Câu trả lời:


331

Bạn đang tìm kiếm collections.defaultdict(có sẵn cho Python 2.5+). Điều này

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

sẽ làm những gì bạn muốn.

Đối với các Python thông thường dict, nếu không có giá trị cho một khóa nhất định, bạn sẽ không nhận được Nonekhi truy cập dict - a KeyErrorsẽ được nâng lên. Vì vậy, nếu bạn muốn sử dụng thường xuyên dict, thay vì mã của bạn, bạn sẽ sử dụng

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1

8
Theo ví dụ của anh ta, nó là đủ để đặt "defaultdict (lambda: 0)" và bỏ qua toàn bộ mệnh đề "if".
Deestan

Điều này hoạt động, nhưng nhầm lẫn các khóa và giá trị (làm cho nó hơi kỳ lạ để đọc). 'some_value' nên là 'some_key'
mikemaccana

@nailer: đã sửa, cảm ơn. Ban đầu tôi đã sử dụng 'some_value' vì đó là tên biến trong câu hỏi, nhưng bây giờ tôi đồng ý nó rõ ràng hơn.
dF.

20
... Hoặc cho dicts thường xuyên , bạn có thể làm my_dict[key] = my_dict.get(key, 0) + 1.
minmaxavg

Làm thế nào để mở rộng điều này cho từ điển lồng nhau? dict [key1] [key2] + = 1?
Pablo Ruiz Ruiz

300

Tôi thích làm điều này trong một dòng mã.

my_dict = {}

my_dict [some_key] = my_dict.get (some_key, 0) + 1

Từ điển có một hàm, get, có hai tham số - khóa bạn muốn và giá trị mặc định nếu nó không tồn tại. Tôi thích phương thức này hơn defaultdict vì bạn chỉ muốn xử lý trường hợp khóa không tồn tại trong một dòng mã này, không phải ở mọi nơi.


1
@AndrewWilkinson xấu của tôi. Không đọc câu trả lời của bạn kỹ lưỡng như tôi nên có.
masaers

59

Cá nhân tôi thích sử dụng setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1

setdefaultnó thật tuyệt. Nó không thay đổi giá trị nếu đã được đặt some_key. Ví dụ, d={1:2}; d.setdefault(1, 0)không làm phiền giá trị của d[1].
wsaleem

49

Bạn cần key in dictthành ngữ cho điều đó.

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

Tuy nhiên, có lẽ bạn nên cân nhắc sử dụng defaultdict(như dF đề xuất).


1
Xin lưu ý rằng trong ít nhất 2,6 has_key () đã được sử dụng để thay thế khóa trong d. Tôi nghĩ rằng đó là cách này trong 2,5 là tốt.
David Locke

Lưu ý rằng người ta có thể viết my_dict[key] is not None, rõ ràng hơn (ít nhất là IMHO)
brandizzi

@brandizzi - đồng ý,if key in my_dict and my_dict[key]:
Rob Grant

18

Để trả lời câu hỏi " làm thế nào tôi có thể tìm ra nếu một chỉ mục nhất định trong chính tả đó đã được đặt thành giá trị không phải là Không ", tôi thích điều này:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

Điều này phù hợp với khái niệm EAFP đã được viện dẫn (dễ dàng hơn để yêu cầu sự tha thứ sau đó cho phép). Nó cũng tránh việc tra cứu khóa trùng lặp trong từ điển như trongkey in my_dict and my_dict[key] is not None rất thú vị nếu tra cứu đắt tiền.

Đối với vấn đề thực tế mà bạn đã đặt ra, tức là tăng int nếu nó tồn tại hoặc đặt nó thành giá trị mặc định, tôi cũng khuyên bạn nên

my_dict[key] = my_dict.get(key, default) + 1

như trong câu trả lời của Andrew Wilkinson.

Có một giải pháp thứ ba nếu bạn đang lưu trữ các đối tượng có thể sửa đổi trong từ điển của bạn. Một ví dụ phổ biến cho điều này là một multimap , nơi bạn lưu trữ một danh sách các yếu tố cho các khóa của bạn. Trong trường hợp đó, bạn có thể sử dụng:

my_dict.setdefault(key, []).append(item)

Nếu một giá trị cho khóa không tồn tại trong từ điển, phương thức setdefault sẽ đặt nó thành tham số thứ hai của setdefault. Nó hoạt động giống như một my_dict [key] tiêu chuẩn, trả về giá trị cho khóa (có thể là giá trị mới được đặt).


Điều thực sự có vẻ Pythonic (với một người ngoài cuộc như tôi) là bất kỳ câu hỏi nào cũng có ít nhất 3 câu trả lời hợp lệ :)
davka

@davka: Chà, ba trường hợp sử dụng gần như giống nhau, nhưng khác nhau: a) tìm xem có phần tử không-không có trong từ điển b) lấy một giá trị từ từ điển hay sử dụng mặc định nếu giá trị không tồn tại c) lấy một giá trị từ từ điển và lưu trữ mặc định nếu giá trị này chưa tồn tại.
nd.

Tôi biết :) đây không phải là một bài phê bình, tôi chỉ thích thú với thực tế này
davka

Trong một bình luận cho câu trả lời của @ ryeguy, Stuart Woodward gợi ý rằng "chi phí xử lý Ngoại lệ trong các ngôn ngữ luôn là một thứ tự lớn hơn so với tra cứu bảng băm xác định xem mục đó có tồn tại hay không trong từ điển", trong khi bạn đang nói "Nó cũng tránh việc tra cứu khóa trùng lặp trong từ điển ... nếu việc tra cứu tốn kém "- có ai có bất kỳ phép đo nào trong đó việc xử lý ngoại lệ nhanh hơn hoặc chậm hơn tra cứu khóa kép không?
Michael Firth

1
@MichaelFirth Tôi đã thực hiện một tìm kiếm đáng chú ý về chi phí ngoại lệ của Python: stackoverflow.com/questions/2522005/. Nó chậm hơn, nhưng không nhiều. Hãy nhớ rằng khái niệm cấp cao về ném Ngoại lệ được xử lý rất khác nhau trong các ngôn ngữ khác nhau và bạn không thể khái quát ưu và nhược điểm. Vì vậy, trong khi "Ngoại lệ có chi phí gấp 10 lần" có thể đúng với Java, thì nó không dành cho Python (hoặc Swift hoặc những người khác).
nd.

13

Đồng ý với cgoldberg. Làm thế nào tôi làm điều đó là:

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

Vì vậy, hoặc làm như trên, hoặc sử dụng một dict mặc định như những người khác đã đề xuất. Đừng sử dụng nếu báo cáo. Đó không phải là Pythonic.


8
Làm thế nào là nếu báo cáo không Pythonic?
Adam Parkin

2
Tôi nghĩ rằng đây là một trường hợp trong đó EAFP của Python không phải là cách tốt nhất. Ví dụ của bạn ở trên có mã trùng lặp; Điều gì nếu một ngày chúng ta muốn +=2hoặc -=1? Bạn phải nhớ để thay đổi cả hai dòng. Bây giờ nó có vẻ như là một điều tầm thường, nhưng đó là những con bọ nhỏ 'tầm thường' ngu ngốc có thể quay lại cắn bạn.
Cam Jackson

3
Điều này có vẻ tốt và hoạt động tốt, nhưng tôi thường tránh làm như vậy bởi vì tôi nghĩ rằng chi phí xử lý Exception trong các ngôn ngữ luôn là một thứ tự lớn hơn so với tra cứu bảng băm xác định liệu mục đó có tồn tại trong từ điển hay không.
Stuart Woodward

11

Như bạn có thể thấy từ nhiều câu trả lời, có một số giải pháp. Một trường hợp của LBYL (xem trước khi bạn nhảy) chưa được đề cập, phương thức has_key ():

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict

6
has_key () chậm hơn toán tử 'in' và ít đọc hơn.
Abgan

9
... và nó đã bị phản đối trong Python 2.6 và bị xóa trong Python 3.
Tim Pietzcker

7

Cách bạn đang cố gắng thực hiện được gọi là LBYL (xem trước khi bạn nhảy), vì bạn đang kiểm tra các điều kiện trước khi cố gắng tăng giá trị của mình.

Cách tiếp cận khác được gọi là EAFP (dễ dàng hơn để xin sự tha thứ sau đó cho phép). Trong trường hợp đó, bạn chỉ cần thử thao tác (tăng giá trị). Nếu thất bại, bạn bắt ngoại lệ và đặt giá trị thành 1. Đây là cách Pythonic hơn một chút để thực hiện (IMO).

http://mail.python.org/pipermail/python-list/2003-May/205182.html


5

Một chút muộn nhưng điều này sẽ làm việc.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1

Wow, là một lập trình viên Java, đây là một cấu trúc trông khá điên rồ. Nó trông giống như một toán tử ternary đặt hàng kỳ lạ?
forresthopkinsa

5

Điều này không trực tiếp trả lời câu hỏi, nhưng với tôi, có vẻ như bạn có thể muốn chức năng của các bộ sưu tập .

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

Điều này dẫn đến kết quả đầu ra

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times

Bộ đếm hoạt động giống như defaultdict(int)với một số chức năng bổ sung để nó hoạt động hoàn hảo khi xử lý độc quyền với số nguyên nhưng bạn không thể hiện bất kỳ hành vi liên quan nào.
Tadhg McDonald-Jensen

4

Đây là một lớp lót mà tôi đã đưa ra gần đây để giải quyết vấn đề này. Nó dựa trên phương thức từ điển setdefault :

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1

0

Tôi đang tìm kiếm nó, không tìm thấy nó trên web sau đó đã thử vận ​​may của mình với Thử / Lỗi và tìm thấy nó

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1

1
Bạn không nên sử dụng __contains__mã sản xuất. btw. __contains__cũng giống như sử dụng is.
dùng1767754

1
my_dict.__contains__(some_key)là tương đương với some_key in my_dict, là quá tải cho innhà điều hành khôngis
Tadhg McDonald-Jensen
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.