@ คำตอบของ Oddthinking ไม่ผิด แต่ฉันคิดว่ามันพลาด จริง , การปฏิบัติเหตุผลหลามมี ABCs ในโลกของเป็ดพิมพ์
วิธีการที่เป็นนามธรรมมีความเรียบร้อย แต่ในความคิดของฉันพวกเขาไม่ได้ใช้กรณีที่ไม่ได้กล่าวถึงในการพิมพ์เป็ด พลังที่แท้จริงของคลาสฐานบทคัดย่ออยู่ในวิธีที่พวกเขาอนุญาตให้คุณปรับแต่งพฤติกรรมของisinstance
issubclass
และ ( __subclasshook__
โดยทั่วไปคือ API ที่เป็นมิตรกว่าบน Python __instancecheck__
และ__subclasscheck__
hooks) การปรับโครงสร้างภายในเพื่อให้ทำงานกับประเภทที่กำหนดเองเป็นส่วนหนึ่งของปรัชญาของ Python
รหัสที่มาของไพ ธ อนเป็นแบบอย่าง นี่คือวิธีที่collections.Container
กำหนดไว้ในไลบรารีมาตรฐาน (ณ เวลาที่เขียน):
class Container(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __contains__(self, x):
return False
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
if any("__contains__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
คำจำกัดความของ __subclasshook__
บอกว่าคลาสใด ๆ ที่มีแอ__contains__
ททริบิวต์ถือเป็นคลาสย่อยของคอนเทนเนอร์แม้ว่ามันจะไม่ได้คลาสย่อยโดยตรงก็ตาม ดังนั้นฉันสามารถเขียนสิ่งนี้:
class ContainAllTheThings(object):
def __contains__(self, item):
return True
>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True
ในคำอื่น ๆ ถ้าคุณใช้อินเทอร์เฟซที่ถูกต้องคุณเป็นคลาสย่อย! ABCs ให้วิธีการอย่างเป็นทางการในการกำหนดส่วนต่อประสานใน Python ในขณะที่ยังคงยึดมั่นในจิตวิญญาณของการพิมพ์แบบเป็ด นอกจากนี้การทำงานในลักษณะที่เกียรตินิยมเปิดปิดหลักการ
แบบจำลองวัตถุของ Python มีลักษณะคล้ายกับระบบ OO แบบ "ดั้งเดิม" มากขึ้น (ซึ่งฉันหมายถึง Java *) - เรามีคลาส yer วัตถุ yer วิธี yer - แต่เมื่อคุณเกาพื้นผิวคุณจะพบบางสิ่งที่สมบูรณ์ยิ่งขึ้นและ ยืดหยุ่นมากขึ้น ในทำนองเดียวกันแนวคิดของ Python เกี่ยวกับคลาสพื้นฐานที่เป็นนามธรรมอาจเป็นที่รู้จักสำหรับนักพัฒนา Java แต่ในทางปฏิบัติพวกเขามีจุดประสงค์เพื่อจุดประสงค์ที่แตกต่างกันมาก
บางครั้งฉันพบว่าตัวเองกำลังเขียนฟังก์ชั่น polymorphic ที่สามารถใช้กับสิ่งของชิ้นเดียวหรือของสะสมได้และฉันก็พบว่า isinstance(x, collections.Iterable)
สามารถอ่านได้มากกว่าhasattr(x, '__iter__')
หรือtry...except
บล็อกที่เทียบเท่า (ถ้าคุณไม่รู้จัก Python สามในนั้นที่จะทำให้รหัสชัดเจนที่สุด)
ที่กล่าวว่าฉันพบว่าฉันไม่ค่อยจำเป็นต้องเขียน ABC ของตัวเองและฉันมักจะค้นพบความต้องการอย่างใดอย่างหนึ่งผ่าน refactoring ถ้าฉันเห็นฟังก์ชัน polymorphic ทำการตรวจสอบคุณสมบัติจำนวนมากหรือฟังก์ชันตรวจสอบคุณสมบัติเดียวกันจำนวนมากกลิ่นนั้นจะบ่งบอกถึงการมีอยู่ของ ABC ที่รอการสกัดอยู่
* โดยไม่มีการอภิปรายว่า Java เป็นระบบ OO แบบ "ดั้งเดิม" หรือไม่ ...
ภาคผนวก : แม้ว่าชั้นฐานนามธรรมสามารถแทนที่ลักษณะการทำงานของisinstance
และissubclass
ก็ยังไม่เข้าสู่MROของ subclass เสมือน นี่คืออันตรายที่อาจเกิดขึ้นสำหรับลูกค้า: ไม่วัตถุที่ทุกคนมีวิธีการที่กำหนดไว้ในisinstance(x, MyABC) == True
MyABC
class MyABC(metaclass=abc.ABCMeta):
def abc_method(self):
pass
@classmethod
def __subclasshook__(cls, C):
return True
class C(object):
pass
# typical client code
c = C()
if isinstance(c, MyABC): # will be true
c.abc_method() # raises AttributeError
น่าเสียดายที่หนึ่งในนั้น "อย่าทำแบบนั้น" กับดัก (ซึ่ง Python มีจำนวนไม่มาก!): หลีกเลี่ยงการกำหนด ABC ด้วยวิธีการทั้งแบบที่__subclasshook__
ไม่ใช่นามธรรม ยิ่งกว่านั้นคุณควรทำให้คำจำกัดความของคุณ__subclasshook__
สอดคล้องกับชุดของวิธีนามธรรมที่ ABC ของคุณกำหนด
__contains__
กับคลาสที่สืบทอดมาcollections.Container
คืออะไร?__str__
ในตัวอย่างของคุณในหลามมีเสมอความเข้าใจร่วมกัน การดำเนินการทำสัญญาเช่นเดียวกับการสืบทอดจากบางส่วนเอบีซีและจากนั้นการดำเนินการ__str__
__str__
ในทั้งสองกรณีคุณสามารถทำลายสัญญาได้ ไม่มีความหมายที่พิสูจน์ได้เช่นสิ่งที่เรามีในการพิมพ์แบบคงที่