คลาสนามธรรมและอินเตอร์เฟสใน Python แตกต่างกันอย่างไร
คลาสนามธรรมและอินเตอร์เฟสใน Python แตกต่างกันอย่างไร
คำตอบ:
สิ่งที่คุณจะเห็นบางครั้งมีดังต่อไปนี้:
class Abstract1( object ):
"""Some description that tells you it's abstract,
often listing the methods you're expected to supply."""
def aMethod( self ):
raise NotImplementedError( "Should have implemented this" )
เนื่องจาก Python ไม่มีสัญญาอินเทอร์เฟซที่เป็นทางการ (และไม่จำเป็น) การแบ่งแยกสไตล์ Java ระหว่างนามธรรมและอินเตอร์เฟสจึงไม่มีอยู่ ถ้ามีคนพยายามกำหนดอินเทอร์เฟซที่เป็นทางการมันจะเป็นคลาสนามธรรม ความแตกต่างจะอยู่ในเจตนาที่ระบุไว้ใน docstring
และความแตกต่างระหว่างนามธรรมและอินเทอร์เฟซคือสิ่งที่น่าทึ่งเมื่อคุณพิมพ์เป็ด
Java ใช้ส่วนต่อประสานเนื่องจากไม่มีหลายมรดก
เนื่องจาก Python มีหลายการสืบทอดคุณอาจเห็นบางสิ่งเช่นนี้
class SomeAbstraction( object ):
pass # lots of stuff - but missing something
class Mixin1( object ):
def something( self ):
pass # one implementation
class Mixin2( object ):
def something( self ):
pass # another
class Concrete1( SomeAbstraction, Mixin1 ):
pass
class Concrete2( SomeAbstraction, Mixin2 ):
pass
สิ่งนี้ใช้ซุปเปอร์คลาสนามธรรมชนิดหนึ่งพร้อมมิกซ์อินเพื่อสร้างคลาสย่อยคอนกรีตที่แยกจากกัน
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
เป็นข้อผิดพลาดข้อมูลเพิ่มเติม :)
คลาสนามธรรมและอินเตอร์เฟสใน Python แตกต่างกันอย่างไร
อินเทอร์เฟซสำหรับวัตถุคือชุดของวิธีการและคุณลักษณะบนวัตถุนั้น
ใน Python เราสามารถใช้คลาสฐานนามธรรมเพื่อกำหนดและบังคับใช้อินเตอร์เฟส
ตัวอย่างเช่นสมมติว่าเราต้องการใช้คลาสฐานนามธรรมอย่างใดอย่างหนึ่งจากcollections
โมดูล:
import collections
class MySet(collections.Set):
pass
ถ้าเราพยายามใช้มันเราจะได้รับTypeError
เพราะคลาสที่เราสร้างไม่รองรับพฤติกรรมที่คาดหวังของเซต:
>>> MySet()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MySet with abstract methods
__contains__, __iter__, __len__
ดังนั้นเราจึงจำเป็นต้องมีการดำเนินการที่น้อย __contains__
, และ__iter__
__len__
ลองใช้ตัวอย่างการนำไปปฏิบัตินี้จากเอกสารประกอบ :
class ListBasedSet(collections.Set):
"""Alternate set implementation favoring space over speed
and not requiring the set elements to be hashable.
"""
def __init__(self, iterable):
self.elements = lst = []
for value in iterable:
if value not in lst:
lst.append(value)
def __iter__(self):
return iter(self.elements)
def __contains__(self, value):
return value in self.elements
def __len__(self):
return len(self.elements)
s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2
เราสามารถสร้างชั้นฐานนามธรรมของเราเองโดยการตั้งค่า metaclass เป็นabc.ABCMeta
และใช้abc.abstractmethod
มัณฑนากรในวิธีการที่เกี่ยวข้อง เมตาคลาสจะเพิ่มฟังก์ชั่นการตกแต่งให้กับแอ__abstractmethods__
ททริบิวเพื่อป้องกันการสร้างอินสแตนซ์จนกว่าจะมีการกำหนด
import abc
ตัวอย่างเช่น "effable" หมายถึงสิ่งที่สามารถแสดงเป็นคำได้ สมมติว่าเราต้องการนิยามคลาสพื้นฐานที่เป็นนามธรรมซึ่งสามารถใช้งานได้ใน Python 2:
class Effable(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
หรือใน Python 3 ที่มีการเปลี่ยนแปลงเล็กน้อยในการประกาศ metaclass:
class Effable(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
ตอนนี้ถ้าเราพยายามที่จะสร้างวัตถุที่ไม่มีผลได้โดยไม่ต้องใช้อินเตอร์เฟส:
class MyEffable(Effable):
pass
และพยายามยกตัวอย่าง:
>>> MyEffable()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
เราได้รับแจ้งว่าเรายังทำงานไม่เสร็จ
ตอนนี้ถ้าเราปฏิบัติตามโดยให้อินเทอร์เฟซที่คาดไว้:
class MyEffable(Effable):
def __str__(self):
return 'expressable!'
จากนั้นเราสามารถใช้คลาสที่เป็นรูปธรรมของคลาสที่ได้มาจากนามธรรมหนึ่ง:
>>> me = MyEffable()
>>> print(me)
expressable!
มีสิ่งอื่น ๆ ที่เราสามารถทำได้เช่นนี้ลงทะเบียน subclasses เสมือนที่ใช้อินเตอร์เฟซเหล่านี้แล้ว แต่ฉันคิดว่ามันอยู่นอกเหนือขอบเขตของคำถามนี้ อย่างไรก็ตามวิธีการอื่นที่แสดงที่นี่จะต้องปรับวิธีนี้โดยใช้abc
โมดูลเพื่อทำเช่นนั้น
เราได้แสดงให้เห็นว่าการสร้าง Abstract Base Class จะกำหนดอินเตอร์เฟสสำหรับวัตถุที่กำหนดเองใน Python
งูหลาม> = 2.6 มีการเรียนบทคัดย่อฐาน
บทคัดย่อ Base Classes (ABCs แบบย่อ) เติมเต็มการพิมพ์เป็ดโดยให้วิธีการกำหนดส่วนต่อประสานเมื่อเทคนิคอื่น ๆ เช่น Hasattr () น่าจะซุ่มซ่าม Python มาพร้อมกับ ABCs ในตัวจำนวนมากสำหรับโครงสร้างข้อมูล (ในโมดูลการรวบรวม), ตัวเลข (ในโมดูลตัวเลข) และกระแสข้อมูล (ในโมดูล io) คุณสามารถสร้าง ABC ของคุณเองด้วยโมดูล abc
นอกจากนี้ยังมีโมดูลZope Interfaceซึ่งใช้โดยโครงการที่อยู่นอก zope เช่น twisted ฉันไม่คุ้นเคยกับมันจริงๆ แต่มีหน้าวิกิที่นี่ที่อาจช่วยได้
โดยทั่วไปคุณไม่จำเป็นต้องมีแนวคิดของคลาสนามธรรมหรืออินเทอร์เฟซใน python (แก้ไข - ดูคำตอบของ S.Lott สำหรับรายละเอียด)
Python ไม่มีแนวคิดใดแนวคิดหนึ่ง
มันใช้การพิมพ์เป็ดซึ่งลบความจำเป็นในการเชื่อมต่อ (อย่างน้อยสำหรับคอมพิวเตอร์ :-))
Python <= 2.5: คลาสพื้นฐานมีอยู่จริง แต่ไม่มีวิธีที่ชัดเจนในการทำเครื่องหมายวิธีเป็น 'pure virtual' ดังนั้นคลาสจึงไม่เป็นนามธรรม
งูหลาม> = 2.6: บทคัดย่อฐานเรียนทำที่มีอยู่ ( http://docs.python.org/library/abc.html ) และอนุญาตให้คุณระบุวิธีการที่ต้องนำไปใช้ในคลาสย่อย ฉันไม่ชอบไวยากรณ์มาก แต่มีคุณลักษณะอยู่ที่นั่น เวลาส่วนใหญ่น่าจะดีกว่าถ้าใช้การพิมพ์เป็ดจากฝั่งไคลเอ็นต์ 'ใช้'
ด้วยวิธีการขั้นพื้นฐานเพิ่มเติมที่จะอธิบาย: อินเทอร์เฟซนั้นคล้ายกับมัฟฟินแพนเปล่า มันเป็นไฟล์คลาสที่มีชุดของคำจำกัดความวิธีการที่ไม่มีรหัส
คลาสนามธรรมเป็นสิ่งเดียวกัน แต่ไม่ใช่ทุกฟังก์ชันที่จะต้องว่างเปล่า บางคนสามารถมีรหัส มันไม่ได้ว่างเปล่าอย่างเคร่งครัด
ทำไมความแตกต่าง: ไม่มีความแตกต่างในทางปฏิบัติมากใน Python แต่ในระดับการวางแผนสำหรับโครงการขนาดใหญ่อาจเป็นเรื่องธรรมดาที่จะพูดคุยเกี่ยวกับอินเทอร์เฟซเนื่องจากไม่มีรหัส โดยเฉพาะอย่างยิ่งถ้าคุณกำลังทำงานกับโปรแกรมเมอร์ Java ที่คุ้นเคยกับคำว่า
โดยทั่วไปแล้วอินเทอร์เฟซจะใช้เฉพาะในภาษาที่ใช้โมเดลคลาสสืบทอดเดี่ยว ในภาษาที่สืบทอดเดียวเหล่านี้โดยทั่วไปแล้วจะใช้อินเทอร์เฟซถ้าคลาสใด ๆ สามารถใช้วิธีการเฉพาะหรือชุดของวิธีการ นอกจากนี้ในภาษาที่สืบทอดเดียวคลาสที่เป็นนามธรรมถูกใช้เพื่อกำหนดตัวแปรคลาสเพิ่มเติมนอกเหนือจากวิธีการใดวิธีหนึ่งหรือมากกว่าหรือเพื่อใช้ประโยชน์จากรูปแบบการสืบทอดเดี่ยวเพื่อ จำกัด ช่วงของคลาสที่สามารถใช้ชุดของเมธอด
ภาษาที่รองรับรูปแบบการสืบทอดหลายแบบมักจะใช้คลาสหรือคลาสพื้นฐานแบบนามธรรมเท่านั้นและไม่ใช่อินเตอร์เฟส เนื่องจาก Python รองรับการสืบทอดหลายแบบมันไม่ได้ใช้อินเตอร์เฟสและคุณต้องการใช้คลาสพื้นฐานหรือคลาสพื้นฐานที่เป็นนามธรรม
คลาส Abstract เป็นคลาสที่มีเมธอด abstract อย่างน้อยหนึ่งวิธี พร้อมกับวิธีการที่เป็นนามธรรมชั้นเรียนที่เป็นนามธรรมสามารถมีวิธีคงที่ระดับและอินสแตนซ์ แต่ในกรณีของอินเตอร์เฟสมันจะมีวิธีการแบบนามธรรมเท่านั้น ดังนั้นจึงไม่จำเป็นต้องสืบทอดคลาสนามธรรม แต่มันเป็นข้อบังคับที่จะสืบทอดส่วนต่อประสาน
เพื่อความสมบูรณ์เราควรพูดถึงPEP3119 ซึ่ง ABC ได้รับการแนะนำและเปรียบเทียบกับอินเทอร์เฟซและความคิดเห็นดั้งเดิมของ Talin
คลาสนามธรรมไม่ใช่อินเตอร์เฟสที่สมบูรณ์แบบ:
แต่ถ้าคุณลองเขียนด้วยวิธีของคุณเอง:
def some_function(self):
raise NotImplementedError()
interface = type(
'your_interface', (object,),
{'extra_func': some_function,
'__slots__': ['extra_func', ...]
...
'__instancecheck__': your_instance_checker,
'__subclasscheck__': your_subclass_checker
...
}
)
ok, rather as a class
or as a metaclass
and fighting with python to achieve the immutable object
and doing refactoring
...
คุณจะรู้ได้อย่างรวดเร็วว่าคุณกำลังคิดค้นวงล้อให้สำเร็จในที่สุด
abc.ABCMeta
abc.ABCMeta
ถูกเสนอให้เป็นส่วนเสริมที่มีประโยชน์ของฟังก์ชั่นอินเทอร์เฟซที่หายไปและมันก็ยุติธรรมพอในภาษาอย่างหลาม
แน่นอนว่ามันสามารถปรับปรุงได้ดีขึ้นในขณะที่เขียนเวอร์ชัน 3 และเพิ่มไวยากรณ์ใหม่และแนวคิดอินเทอร์เฟซที่ไม่เปลี่ยนแปลง ...
สรุป:
The abc.ABCMeta IS "pythonic" interface in python