หากคุณกำลังจัดการกับคลาสตั้งแต่หนึ่งคลาสขึ้นไปซึ่งคุณไม่สามารถเปลี่ยนจากภายในได้มีวิธีทั่วไปและวิธีง่าย ๆ ที่จะทำเช่นนี้ซึ่งไม่ได้ขึ้นอยู่กับไลบรารีที่แตกต่างกัน:
วิธีที่ง่ายที่สุดไม่ปลอดภัยสำหรับวัตถุที่ซับซ้อนมาก
pickle.dumps(a) == pickle.dumps(b)
pickle
คือการทำให้เป็นอนุกรมทั่วไปสำหรับวัตถุ Python และจะสามารถทำให้เป็นอนุกรมอะไรก็ได้จริง ๆ ในตัวอย่างข้างต้นผมเปรียบเทียบstr
จากอันดับหนึ่งจากa
b
วิธีนี้แตกต่างจากวิธีถัดไปวิธีนี้มีข้อดีของการตรวจสอบคลาสแบบกำหนดเองด้วยเช่นกัน
ความยุ่งยากที่ใหญ่ที่สุด: เนื่องจากการสั่งซื้อเฉพาะและวิธีการเข้ารหัส [de / en] pickle
อาจไม่ให้ผลลัพธ์ที่เหมือนกันสำหรับวัตถุที่เท่ากันโดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับวัตถุที่ซับซ้อนมากขึ้น (เช่นรายการของอินสแตนซ์คลาสแบบกำหนดเองซ้อน) ใน libs บุคคลที่สามบางอย่าง สำหรับกรณีเหล่านั้นฉันขอแนะนำวิธีการอื่น:
วิธีการที่ปลอดภัยสำหรับวัตถุใด ๆ อย่างละเอียด
คุณสามารถเขียนภาพสะท้อนแบบเรียกซ้ำซึ่งจะให้วัตถุที่เป็นอนุกรมแล้วเปรียบเทียบผลลัพธ์
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:])
ตอนนี้ไม่สำคัญว่าวัตถุของคุณจะเป็นอะไร
>>> 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
จำนวนของการเปรียบเทียบไม่สำคัญเช่นกัน
>>> c = RandomForestClassifier(max_depth=2, random_state=1000)
>>> deep_equals(a, b, c)
False
กรณีการใช้งานของฉันคือการตรวจสอบความเท่าเทียมกันอย่างลึกล้ำระหว่างชุดการเรียนรู้ของเครื่องเรียนรู้ที่ผ่านการฝึกอบรมแล้วในการทดสอบ BDD โมเดลเป็นของ libs บุคคลที่สามที่หลากหลาย ดำเนินการอย่างแน่นอน__eq__
อย่างเหมือนกับคำตอบอื่น ๆ ที่นี่แนะนำไม่ใช่ตัวเลือกสำหรับฉัน
ครอบคลุมทุกฐาน
คุณอาจจะอยู่ในสถานการณ์ที่หนึ่งหรือมากกว่าหนึ่งของการเรียนที่กำหนดเองที่มีการเทียบไม่ได้มี__dict__
การดำเนินการ นั่นคือไม่ธรรมดาโดยวิธีใด ๆ แต่มันเป็นกรณีของการย่อยภายใน sklearn <type 'sklearn.tree._tree.Tree'>
ของสุ่มป่าลักษณนามที่: ปฏิบัติต่อสถานการณ์เหล่านี้เป็นกรณี ๆ ไป - เช่นโดยเฉพาะฉันตัดสินใจที่จะแทนที่เนื้อหาของประเภทที่เป็นปัญหาด้วยเนื้อหาของวิธีการที่ให้ข้อมูลตัวแทนแก่ฉันในอินสแตนซ์ (ในกรณีนี้คือ__getstate__
วิธีการ) สำหรับเช่นแถวที่สองถึงครั้งสุดท้ายในbase_typed
กลายเป็น
d = obj if T is dict else obj.__dict__ if '__dict__' in dir(obj) else obj.__getstate__()
แก้ไข: เพื่อประโยชน์ขององค์กรฉันได้แทนที่สองบรรทัดสุดท้ายbase_typed
ด้วยreturn dict_from(obj)
และใช้การสะท้อนทั่วไปเพื่อรองรับ libs ที่คลุมเครือมากขึ้น (ฉันกำลังมองหาคุณ 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()}
อย่าคำนึงถึงวิธีการใด ๆ ข้างต้นที่ให้ผลลัพธ์True
สำหรับออบเจ็กต์ที่ต่างกันด้วยคู่คีย์ - ค่าเดียวกัน
>>> a = {'foo':[], 'bar':{}}
>>> b = {'bar':{}, 'foo':[]}
>>> pickle.dumps(a) == pickle.dumps(b)
False
แต่ถ้าคุณต้องการให้คุณใช้sorted
วิธีการในตัวของ Python ล่วงหน้า
return NotImplemented
(แทนที่จะเพิ่มNotImplementedError
) ครอบคลุมหัวข้อดังกล่าวที่นี่: stackoverflow.com/questions/878943/…