2015-06-13 21 views
24

kiểm tra cho bình đẳng hoạt động tốt như thế này cho dicts python:Kiểm tra xem dict chứa trong dict

first = {"one":"un", "two":"deux", "three":"trois"} 
second = {"one":"un", "two":"deux", "three":"trois"} 

print(first == second) # Result: True 

Nhưng bây giờ dict thứ hai của tôi có chứa một số phím bổ sung Tôi muốn bỏ qua:

first = {"one":"un", "two":"deux", "three":"trois"} 
second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 

Is có một cách đơn giản để kiểm tra nếu dict đầu tiên là một phần của dict thứ hai, với tất cả các khóa và giá trị của nó?

EDIT 1:

Câu hỏi này được nghi ngờ là một bản sao của How to test if a dictionary contains certain keys, nhưng tôi quan tâm đến việc kiểm tra phím và giá trị của họ. Chỉ chứa các khóa giống nhau không làm cho hai dicts bằng nhau.

EDIT 2:

OK, tôi có một số câu trả lời hiện nay sử dụng bốn phương pháp khác nhau, và đã chứng minh tất cả trong số họ làm việc. Khi tôi cần một quy trình nhanh, tôi đã kiểm tra từng thời gian thực hiện. Tôi tạo ra ba chữ cái giống hệt nhau với 1000 mục, khóa và giá trị là các chuỗi ngẫu nhiên có độ dài 10. Các số secondthird có một số cặp khóa-giá trị bổ sung và khóa ngoài không phải là third có giá trị mới. Vì vậy, first là một tập con của second, nhưng không phải là third. Sử dụng mô-đun timeit với 10000 lần lặp lại, tôi nhận:

Method              Time [s] 
first.viewitems() <=second.viewitems()       0.9 
set(first.items()).issubset(second.items())      7.3 
len(set(first.items()) & set(second.items())) == len(first)  8.5 
all(first[key] == second.get(key, sentinel) for key in first) 6.0 

tôi đoán phương pháp cuối cùng là chậm nhất, nhưng đó là về địa điểm 2. Nhưng phương pháp 1 nhịp đập tất cả chúng.

Cảm ơn câu trả lời của bạn!

+1

thể trùng lặp của [Làm thế nào để kiểm tra xem một cuốn từ điển chứa một số phím] (http://stackoverflow.com/questions/3415347/how-to-test-if-a-dictionary-contains-certain-keys) – tjati

Trả lời

44

Bạn có thể sử dụng một dictionary view:

# Python 2 
if first.viewitems() <= second.viewitems(): 
    # true only if `first` is a subset of `second` 

# Python 3 
if first.items() <= second.items(): 
    # true only if `first` is a subset of `second` 

quan điểm điển là standard in Python 3, trong Python 2, bạn cần tiền tố các phương thức chuẩn với view. Chúng hoạt động như các bộ và các thử nghiệm <= nếu một trong số đó là tập con của (hoặc bằng) khác.

Demo bằng Python 3:

>>> first = {"one":"un", "two":"deux", "three":"trois"} 
>>> second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 
>>> first.items() <= second.items() 
True 
>>> first['four'] = 'quatre' 
>>> first.items() <= second.items() 
False 

này làm việc cho giá trị phi hashable quá, như các phím làm cho cặp khóa-giá trị duy nhất rồi. Các tài liệu là một chút bối rối ở điểm này, nhưng ngay cả với các giá trị có thể thay đổi (ví dụ, danh sách) hoạt động này:

>>> first_mutable = {'one': ['un', 'een', 'einz'], 'two': ['deux', 'twee', 'zwei']} 
>>> second_mutable = {'one': ['un', 'een', 'einz'], 'two': ['deux', 'twee', 'zwei'], 'three': ['trois', 'drie', 'drei']} 
>>> first_mutable.items() <= second_mutable.items() 
True 
>>> first_mutable['one'].append('ichi') 
>>> first_mutable.items() <= second_mutable.items() 
False 

Bạn cũng có thể sử dụng all() function với một biểu thức máy phát điện; sử dụng object() như một trọng điểm để phát hiện các giá trị thiếu chính xác:

sentinel = object() 
if all(first[key] == second.get(key, sentinel) for key in first): 
    # true only if `first` is a subset of `second` 

nhưng điều này không phải là có thể đọc được và biểu cảm như sử dụng xem từ điển.

4

Vì vậy, về cơ bản bạn muốn kiểm tra xem một từ điển có phải là tập hợp con của một từ điển khác hay không.

first = {"one":"un", "two":"deux", "three":"trois"} 
second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 

def subset_dic(subset, superset): 
    return len(set(subset.items()) & set(superset.items())) == len(subset) 


print(subset_dic(first, second)) 

Prints:

True 

Trong trường hợp bạn muốn trừu tượng ra phần tập hợp con/superset:

def subset_dic(dict1, dict2): 
    return len(set(dict1.items()) & set(dict2.items())) == len(min((dict1, dict2), key=len)) 

Note: điều này sẽ không làm việc, nếu có giá trị là một đối tượng có thể thay đổi. Do đó bạn có thể thêm một bước bổ sung (chuyển đổi đối tượng có thể biến đổi thành analog bất biến) trong hàm để vượt qua giới hạn này.

+0

Tạo một bộ từ điển đầy đủ có vẻ hơi tốn kém. – poke

+0

So sánh hai bộ bộ dữ liệu mà không cần băm thậm chí còn đắt hơn về độ phức tạp tuyệt đối. –

+0

Có, nhưng không cần phải so sánh các bộ dữ liệu; từ điển đã có quyền truy cập mục O (1), vì vậy bạn chỉ có thể lặp qua một từ điển và thực hiện kiểm tra thành viên trên một từ điển khác. – poke

6
all(k in second and second[k] == v for k, v in first.items()) 

nếu bạn biết rằng không ai trong số các giá trị có thể None, nó sẽ đơn giản hóa để:

all(second.get(k, None) == v for k, v in first.items()) 
+0

Tại sao bạn xóa phiên bản thay thế mà bạn đã đăng trước đó? 'not (set (first.items()) - set (second.items()))' –

+0

@Iskren vì nó sẽ không hoạt động nếu các giá trị không được băm, ví dụ nếu ''foo': [1, 2, 3 ] 'là một trong những mục. –

+1

Giải pháp thứ hai của bạn rất thanh lịch, mặc dù nó có thể bị chậm nếu các giá trị được lồng sâu. Tuy nhiên nó rất đa hình và súc tích. +1 –

2

# Ans Cập nhật:

PHƯƠNG PHÁP-1: Sử dụng từ điển Views:

Như Martijn đề nghị, chúng ta có thể sử dụng xem từ điển để kiểm tra điều này. dict.viewitems() hoạt động như một bộ. Chúng tôi có thể thực hiện hoạt động thiết lập khác nhau trên ngã tư này như thế nào, công đoàn vv (Kiểm tra này link.)

first.viewitems() <= second.viewitems() 
True 

Chúng tôi kiểm tra nếu first là ít hơn bằng second. Đây đánh giá là True phương tiện first là một tập hợp con của second.

PHƯƠNG PHÁP-2 Sử dụng issubset() hoạt động của bộ:

(DISCLAIMER: Phương pháp Phương pháp này có một số khả năng dự phòng và yêu cầu tất cả các giá trị được hashable. -1 được đề nghị để được theo sau để xử lý tất cả các trường hợp. Cảm ơn Martijn những lời đề nghị.)

sử dụng .items() thuộc tính của một cuốn từ điển để có được một danh sách (key, value) tuples và sau đó sử dụng issubset() hoạt động của bộ.

Điều này sẽ kiểm tra cả khóa và tính bình đẳng..

>>> first = {"one":"un", "two":"deux", "three":"trois"} 
>>> second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 

>>> set(first.items()).issubset(second.items()) 
True 
+1

Tại sao sử dụng 'list()' trước và không sử dụng 'set (first.items()). Issubset (second.items())' trực tiếp? Và trong Python 3, 'dict.items()' * hỗ trợ trực tiếp usecase *; 'l1.items()

+0

Cảm ơn Martijn. Bước đó là không cần thiết. Đã cập nhật ans! –

+0

Bạn vẫn còn dư thừa ở đây; trong Python 3 'dict.items()' * đã hoạt động như một tập hợp *. Trong Python 2 bạn có thể nhận được cùng một hành vi bằng cách sử dụng 'dict.viewitems()'. Và cách tiếp cận của bạn vẫn yêu cầu các giá trị là có thể băm, trong khi các chế độ xem từ điển thì không. –

Các vấn đề liên quan