Nếu bạn đang làm việc với một hoặc nhiều lớp mà bạn không thể thay đổi từ bên trong, có những cách đơn giản và chung chung để làm điều này cũng không phụ thuộc vào một thư viện khác biệt:
Phương pháp dễ nhất, không an toàn cho các đối tượng rất phức tạp
pickle.dumps(a) == pickle.dumps(b)
pickle
là một lib tuần tự hóa rất phổ biến cho các đối tượng Python, và do đó sẽ có thể tuần tự hóa khá nhiều thứ, thực sự. Trong đoạn trích trên, tôi đang so sánh str
từ từ nối tiếp a
với từb
. Không giống như phương thức tiếp theo, phương thức này có ưu điểm là cũng kiểm tra các lớp tùy chỉnh.
Rắc rối lớn nhất: do cách đặt hàng cụ thể và phương pháp mã hóa [de / en], pickle
có thể không mang lại kết quả tương tự cho các đối tượng bằng nhau , đặc biệt khi xử lý các đối tượng phức tạp hơn (ví dụ: danh sách các trường hợp lớp tùy chỉnh lồng nhau) như bạn thường thấy trong một số libs của bên thứ ba. Đối với những trường hợp đó, tôi muốn giới thiệu một cách tiếp cận khác:
Phương pháp triệt để, an toàn cho mọi đối tượng
Bạn có thể viết một phản xạ đệ quy sẽ cung cấp cho bạn các đối tượng tuần tự hóa, và sau đó so sánh kết quả
from collections.abc import Iterable
BASE_TYPES = [str, int, float, bool, type(None)]
def base_typed(obj):
"""Recursive reflection method to convert any object property into a comparable form.
"""
T = type(obj)
from_numpy = T.__module__ == 'numpy'
if T in BASE_TYPES or callable(obj) or (from_numpy and not isinstance(T, Iterable)):
return obj
if isinstance(obj, Iterable):
base_items = [base_typed(item) for item in obj]
return base_items if from_numpy else T(base_items)
d = obj if T is dict else obj.__dict__
return {k: base_typed(v) for k, v in d.items()}
def deep_equals(*args):
return all(base_typed(args[0]) == base_typed(other) for other in args[1:])
Bây giờ không quan trọng đối tượng của bạn là gì, sự bình đẳng sâu sắc được đảm bảo để làm việc
>>> from sklearn.ensemble import RandomForestClassifier
>>>
>>> a = RandomForestClassifier(max_depth=2, random_state=42)
>>> b = RandomForestClassifier(max_depth=2, random_state=42)
>>>
>>> deep_equals(a, b)
True
Số lượng so sánh không quan trọng là tốt
>>> c = RandomForestClassifier(max_depth=2, random_state=1000)
>>> deep_equals(a, b, c)
False
Trường hợp sử dụng của tôi cho việc này là kiểm tra sự bình đẳng sâu sắc giữa một loạt các mô hình Machine Learning đã được đào tạo trong các bài kiểm tra BDD. Các mô hình thuộc về một loạt các libs của bên thứ ba. Chắc chắn thực hiện __eq__
như các câu trả lời khác ở đây cho thấy không phải là một lựa chọn cho tôi.
Bao gồm tất cả các cơ sở
Bạn có thể đang ở trong một kịch bản trong đó một hoặc nhiều lớp tùy chỉnh được so sánh không có __dict__
triển khai . Điều đó không phổ biến bởi bất kỳ phương tiện nào, nhưng đó là trường hợp của một kiểu con trong phân loại Rừng ngẫu nhiên của sklearn : <type 'sklearn.tree._tree.Tree'>
. Xử lý các tình huống này trong từng trường hợp cụ thể - ví dụ cụ thể , tôi quyết định thay thế nội dung của loại bị ảnh hưởng bằng nội dung của phương thức cung cấp cho tôi thông tin đại diện về trường hợp (trong trường hợp này là __getstate__
phương pháp). Vì vậy, hàng thứ hai đến cuối cùng base_typed
đã trở thành
d = obj if T is dict else obj.__dict__ if '__dict__' in dir(obj) else obj.__getstate__()
Chỉnh sửa: vì lợi ích của tổ chức, tôi đã thay thế hai dòng cuối cùng base_typed
bằng return dict_from(obj)
và thực hiện một phản ánh thực sự chung chung cho nó để phù hợp với các lib tối nghĩa hơn (Tôi đang nhìn vào bạn, Doc2Vec)
def isproperty(prop, obj):
return not callable(getattr(obj, prop)) and not prop.startswith('_')
def dict_from(obj):
"""Converts dict-like objects into dicts
"""
if isinstance(obj, dict):
# Dict and subtypes are directly converted
d = dict(obj)
elif '__dict__' in dir(obj):
d = obj.__dict__
elif str(type(obj)) == 'sklearn.tree._tree.Tree':
# Replaces sklearn trees with their state metadata
d = obj.__getstate__()
else:
# Extract non-callable, non-private attributes with reflection
kv = [(p, getattr(obj, p)) for p in dir(obj) if isproperty(p, obj)]
d = {k: v for k, v in kv}
return {k: base_typed(v) for k, v in d.items()}
Đừng bận tâm, không có phương thức nào ở trên mang lại True
cho các đối tượng khác nhau có cùng cặp khóa-giá trị nhưng thứ tự khóa / giá trị khác nhau, như trong
>>> a = {'foo':[], 'bar':{}}
>>> b = {'bar':{}, 'foo':[]}
>>> pickle.dumps(a) == pickle.dumps(b)
False
Nhưng nếu bạn muốn rằng bạn có thể sử dụng sorted
phương thức tích hợp sẵn của Python trước.
return NotImplemented
(thay vì nuôiNotImplementedError
). Chủ đề đó được đề cập ở đây: stackoverflow.com/questions/878943/ từ