Sự khác biệt giữa lớp trừu tượng và giao diện trong Python


Câu trả lời:


618

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.


5
S. Lott, ý bạn là vì vịt gõ, sự phân biệt giữa has-a (giao diện) và is-a (thừa kế) là không đáng kể?
Lorenzo

3
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. Tôi không biết "đáng kể" nghĩa là gì. Đó là "thực tế" - nó có bản chất - từ quan điểm thiết kế. Nhưng từ góc độ ngôn ngữ có thể không có hỗ trợ. Bạn có thể áp dụng các quy ước để phân biệt giữa một lớp trừu tượng và một định nghĩa lớp giao diện trong Python.
S.Lott

26
@ L.DeLeo - bạn có chắc rằng khái niệm has-a vs. is-a là chính xác không? Tôi thường xem phân biệt là has-a = biến thành viên so với is-a = thừa kế (lớp cha hoặc giao diện). Hãy suy nghĩ so sánh hoặc danh sách trong Java; đó là những mối quan hệ - bất kể chúng là giao diện hay lớp trừu tượng.
dimo414

43
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))là thông báo lỗi nhiều thông tin hơn :)
naught101

9
@Lorenzo một mối quan hệ có một mối quan hệ không liên quan gì đến thừa kế, gõ vịt, giao diện và các lớp trừu tượng (cả bốn đều đề cập đến mối quan hệ is-a).
Karl Richter

197

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.

Sử dụng một lớp cơ sở trừu tượng

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ừ collectionsmô-đ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 TypeErrorvì 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____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

Thực hiện: Tạo một lớp cơ sở trừu tượng

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.ABCMetavà sử dụng trình abc.abstractmethodtrang 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 abcmô-đun để làm như vậy.

Phần kết luận

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.


101

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).


2
Bạn đạt được gì khi sử dụng ABC trong Python?
CpILL

38

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'.


3
Python 3.0 không thêm các lớp cơ sở trừu tượng thực sự. Chúng được sử dụng trong các bộ sưu tập mô-đun cũng như những nơi khác. docs.python.org/3.0/l Library / abc.html
Lara Dougan

Một tài liệu tham khảo về lý do tại sao gõ vịt loại bỏ nhu cầu về giao diện sẽ hữu ích. Đối với tôi, việc gõ vịt, mà tôi hiểu là khả năng "chọc" bất kỳ phương thức hoặc thuộc tính nào trên bất kỳ đối tượng nào, có nghĩa là bạn không cần chỉ định các hành vi cần thiết (và yêu cầu trình biên dịch nhắc nhở bạn để thực hiện chúng), đó là cách tôi hiểu các lớp cơ sở trừu tượng.
Reb.Cabin

Nó ít gõ vịt hơn, sau đó hỗ trợ cho nhiều kế thừa loại bỏ đường nhân tạo giữa giao diện và lớp trừu tượng, ví dụ như Java đã rút ra.
masi

35

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.


+1 cho sự khác biệt trong đó ABC có thể có các triển khai của riêng họ - có vẻ như là một cách tuyệt vời để vượt qua chính mình
Titou

17

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.

http://docs.python.org/l Library / abc.html


2

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.


1

Để 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:

  • thuộc hệ thống phân cấp thừa kế
  • là đột biến

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.