Jika Anda berurusan dengan satu atau beberapa kelas yang tidak dapat Anda ubah dari dalam, ada cara umum dan sederhana untuk melakukan ini yang juga tidak bergantung pada pustaka khusus:
Metode objek termudah, tidak aman untuk objek yang sangat kompleks
pickle.dumps(a) == pickle.dumps(b)
pickleadalah lib serialisasi yang sangat umum untuk objek Python, dan dengan demikian akan dapat membuat serialisasi apa saja, sungguh. Dalam cuplikan di atas saya membandingkan strdari serial adengan yang dari b. Berbeda dengan metode selanjutnya, yang satu ini memiliki keuntungan juga mengetik kelas kustom.
Kerumitan terbesar: karena pemesanan khusus dan metode pengkodean, pickletidak dapat menghasilkan hasil yang sama untuk objek yang sama , khususnya ketika berhadapan dengan yang lebih kompleks (mis. Daftar instance kelas kustom bersarang) seperti Anda akan sering menemukan di beberapa lib pihak ketiga. Untuk kasus-kasus itu, saya akan merekomendasikan pendekatan yang berbeda:
Metode objek yang menyeluruh, aman untuk apa saja
Anda bisa menulis refleksi rekursif yang akan memberi Anda objek serial, dan kemudian membandingkan hasilnya
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:])
Sekarang tidak masalah apa objek Anda, kesetaraan yang dalam dijamin untuk bekerja
>>> 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
Jumlah yang sebanding tidak masalah juga
>>> c = RandomForestClassifier(max_depth=2, random_state=1000)
>>> deep_equals(a, b, c)
False
Kasus penggunaan saya untuk ini adalah memeriksa kesetaraan yang mendalam di antara beragam model Pembelajaran Mesin yang sudah terlatih dalam tes BDD. Model-model tersebut milik beragam set lib pihak ketiga. Tentunya menerapkan __eq__seperti jawaban lain di sini menyarankan bukan pilihan bagi saya.
Menutupi semua pangkalan
Anda mungkin berada dalam skenario di mana satu atau beberapa kelas khusus yang dibandingkan tidak memiliki __dict__implementasi . Itu tidak umum dengan cara apapun, tapi itu adalah kasus subtipe dalam classifier Acak Hutan sklearn ini: <type 'sklearn.tree._tree.Tree'>. Perlakukan situasi ini dalam kasus per kasus - misalnya secara spesifik , saya memutuskan untuk mengganti konten dari jenis yang diderita dengan konten dari metode yang memberi saya informasi yang representatif pada contoh (dalam kasus ini, __getstate__metode). Untuk itu, baris kedua ke terakhir base_typedmenjadi
d = obj if T is dict else obj.__dict__ if '__dict__' in dir(obj) else obj.__getstate__()
Sunting: demi organisasi, saya mengganti dua baris terakhir base_typeddengan return dict_from(obj), dan menerapkan refleksi yang benar-benar umum untuk mengakomodasi lebih banyak lib yang tidak jelas (Saya melihat Anda, 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()}
Jangan pedulikan metode di atas menghasilkan Trueobjek yang berbeda dengan pasangan nilai kunci yang sama tetapi pesanan kunci / nilai yang berbeda, seperti pada
>>> a = {'foo':[], 'bar':{}}
>>> b = {'bar':{}, 'foo':[]}
>>> pickle.dumps(a) == pickle.dumps(b)
False
Tetapi jika Anda mau, Anda bisa menggunakan sortedmetode bawaan Python sebelumnya.
return NotImplemented(bukannya meningkatkanNotImplementedError). Topik itu dibahas di sini: stackoverflow.com/questions/878943/…