Câu trả lời:
Những gì bạn sẽ thấy đôi khi là như sau:
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" )
Vì Python không có (và không cần) hợp đồng Giao diện chính thức, nên sự khác biệt theo kiểu Java giữa trừu tượng hóa và giao diện không tồn tại. Nếu ai đó trải qua nỗ lực để xác định một giao diện chính thức, nó cũng sẽ là một lớp trừu tượng. Sự khác biệt duy nhất sẽ là trong ý định đã nêu trong chuỗi.
Và sự khác biệt giữa trừu tượng và giao diện là một điều tách tóc khi bạn gõ vịt.
Java sử dụng các giao diện vì nó không có nhiều kế thừa.
Bởi vì Python có nhiều sự kế thừa, bạn cũng có thể thấy một cái gì đó như thế này
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
Điều này sử dụng một loại siêu lớp trừu tượng với mixin để tạo các lớp con cụ thể rời rạc.
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
là thông báo lỗi nhiều thông tin hơn :)
Sự khác biệt giữa lớp trừu tượng và giao diện trong Python là gì?
Một giao diện, cho một đối tượng, là một tập hợp các phương thức và thuộc tính trên đối tượng đó.
Trong Python, chúng ta có thể sử dụng một lớp cơ sở trừu tượng để định nghĩa và thực thi một giao diện.
Ví dụ, giả sử chúng ta muốn sử dụng một trong các lớp cơ sở trừu tượng từ collections
mô-đun:
import collections
class MySet(collections.Set):
pass
Nếu chúng tôi cố gắng sử dụng nó, chúng tôi nhận được một TypeError
vì lớp chúng tôi tạo không hỗ trợ hành vi dự kiến của các bộ:
>>> 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__
Vì vậy, chúng tôi được yêu cầu phải thực hiện tại ít nhất __contains__
, __iter__
và __len__
. Hãy sử dụng ví dụ triển khai này từ tài liệu :
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
Chúng ta có thể tạo Lớp cơ sở trừu tượng của riêng mình bằng cách đặt siêu dữ liệu abc.ABCMeta
và sử dụng trình abc.abstractmethod
trang trí trên các phương thức có liên quan. Siêu dữ liệu sẽ được thêm các chức năng trang trí vào __abstractmethods__
thuộc tính, ngăn chặn việc khởi tạo cho đến khi chúng được xác định.
import abc
Ví dụ: "effable" được định nghĩa là một cái gì đó có thể diễn tả bằng lời. Giả sử chúng tôi muốn định nghĩa một lớp cơ sở trừu tượng có thể thực hiện được, trong Python 2:
class Effable(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Hoặc trong Python 3, với sự thay đổi nhỏ trong khai báo siêu dữ liệu:
class Effable(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Bây giờ nếu chúng ta cố gắng tạo một đối tượng có thể thực hiện được mà không thực hiện giao diện:
class MyEffable(Effable):
pass
và cố gắng khởi tạo nó:
>>> MyEffable()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
Chúng tôi được thông báo rằng chúng tôi chưa hoàn thành công việc.
Bây giờ nếu chúng tôi tuân thủ bằng cách cung cấp giao diện dự kiến:
class MyEffable(Effable):
def __str__(self):
return 'expressable!'
sau đó chúng ta có thể sử dụng phiên bản cụ thể của lớp xuất phát từ bản tóm tắt:
>>> me = MyEffable()
>>> print(me)
expressable!
Có những thứ khác chúng ta có thể làm với điều này, như đăng ký các lớp con ảo đã triển khai các giao diện này, nhưng tôi nghĩ rằng điều đó nằm ngoài phạm vi của câu hỏi này. Tuy nhiên, các phương pháp khác được trình bày ở đây sẽ phải điều chỉnh phương thức này bằng cách sử dụng abc
mô-đun để làm như vậy.
Chúng tôi đã chứng minh rằng việc tạo ra một lớp cơ sở trừu tượng xác định các giao diện cho các đối tượng tùy chỉnh trong Python.
Python> = 2.6 có các lớp cơ sở trừu tượng .
Các lớp cơ sở trừu tượng (viết tắt ABC) bổ sung cho việc gõ vịt bằng cách cung cấp một cách để xác định giao diện khi các kỹ thuật khác như hasattr () sẽ vụng về. Python đi kèm với nhiều ABC dựng sẵn cho cấu trúc dữ liệu (trong mô đun bộ sưu tập), số (trong mô đun số) và luồng (trong mô đun io). Bạn có thể tạo ABC của riêng bạn với mô-đun abc.
Ngoài ra còn có mô-đun Giao diện Zope , được sử dụng bởi các dự án bên ngoài zope, giống như xoắn. Tôi không thực sự quen thuộc với nó, nhưng có một trang wiki ở đây có thể giúp ích.
Nói chung, bạn không cần khái niệm về các lớp trừu tượng hoặc giao diện trong python (được chỉnh sửa - xem câu trả lời của S.Lott để biết chi tiết).
Python không thực sự có khái niệm nào cả.
Nó sử dụng kiểu gõ vịt, loại bỏ nhu cầu về giao diện (ít nhất là cho máy tính :-))
Python <= 2.5: Các lớp cơ sở rõ ràng tồn tại, nhưng không có cách rõ ràng nào để đánh dấu một phương thức là 'thuần ảo', vì vậy lớp này không thực sự trừu tượng.
Python> = 2.6: Các lớp cơ sở trừu tượng tồn tại ( http://docs.python.org/l Library / abc.html ). Và cho phép bạn chỉ định các phương thức phải được thực hiện trong các lớp con. Tôi không thích cú pháp lắm, nhưng tính năng là có. Hầu hết thời gian có lẽ tốt hơn để sử dụng gõ vịt từ phía khách hàng 'sử dụng'.
Theo một cách cơ bản hơn để giải thích: Một giao diện giống như một cái chảo muffin trống rỗng. Đó là một tệp lớp với một tập hợp các định nghĩa phương thức không có mã.
Một lớp trừu tượng là điều tương tự, nhưng không phải tất cả các hàm cần phải trống. Một số có thể có mã. Nó không hoàn toàn trống rỗng.
Tại sao lại khác biệt: Không có nhiều khác biệt thực tế trong Python, nhưng ở cấp độ lập kế hoạch cho một dự án lớn, có thể phổ biến hơn để nói về giao diện, vì không có mã. Đặc biệt là nếu bạn đang làm việc với các lập trình viên Java đã quen với thuật ngữ này.
Nói chung, các giao diện chỉ được sử dụng trong các ngôn ngữ sử dụng mô hình lớp kế thừa đơn. Trong các ngôn ngữ kế thừa đơn này, các giao diện thường được sử dụng nếu bất kỳ lớp nào có thể sử dụng một phương thức cụ thể hoặc tập hợp các phương thức. Ngoài ra, trong các ngôn ngữ thừa kế đơn này, các lớp trừu tượng được sử dụng để xác định các biến lớp ngoài không có hoặc nhiều phương thức hoặc để khai thác mô hình thừa kế đơn để giới hạn phạm vi các lớp có thể sử dụng một tập các phương thức.
Các ngôn ngữ hỗ trợ mô hình đa kế thừa có xu hướng chỉ sử dụng các lớp hoặc các lớp cơ sở trừu tượng và không giao diện. Vì Python hỗ trợ nhiều kế thừa, nó không sử dụng giao diện và bạn sẽ muốn sử dụng các lớp cơ sở hoặc các lớp cơ sở trừu tượng.
Các lớp trừu tượng là các lớp có chứa một hoặc nhiều phương thức trừu tượng. Cùng với các phương thức trừu tượng, các lớp Trừu tượng có thể có các phương thức tĩnh, lớp và thể hiện. Nhưng trong trường hợp giao diện, nó sẽ chỉ có các phương thức trừu tượng chứ không phải khác. Do đó, nó không bắt buộc phải kế thừa lớp trừu tượng nhưng bắt buộc phải kế thừa giao diện.
Để hoàn thiện, chúng ta nên đề cập đến PEP3119 nơi ABC được giới thiệu và so sánh với các giao diện, và nhận xét ban đầu của Talin .
Lớp trừu tượng không phải là giao diện hoàn hảo:
Nhưng nếu bạn xem xét việc viết nó theo cách riêng của bạn:
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
...
bạn sẽ khá nhanh nhận ra rằng bạn đang phát minh ra bánh xe để cuối cùng đạt được
abc.ABCMeta
abc.ABCMeta
đã được đề xuất như một sự bổ sung hữu ích của chức năng giao diện còn thiếu và điều đó đủ công bằng trong một ngôn ngữ như python.
Chắc chắn, nó đã có thể được cải thiện tốt hơn trong khi viết phiên bản 3, và thêm cú pháp mới và khái niệm giao diện bất biến ...
Phần kết luận:
The abc.ABCMeta IS "pythonic" interface in python