Làm cách nào để triển khai giao diện trong python?


182
public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

    public void show()
    {
        Console.WriteLine("Hello World!");
    }

    #endregion
}

Làm cách nào để triển khai Python tương đương với mã C # này?

class IInterface(object):
    def __init__(self):
        pass

    def show(self):
        raise Exception("NotImplementedException")


class MyClass(IInterface):
   def __init__(self):
       IInterface.__init__(self)

   def show(self):
       print 'Hello World!'

Đây có phải là một ý tưởng tốt?? Hãy cho ví dụ trong câu trả lời của bạn.


Mục đích sử dụng giao diện trong trường hợp của bạn là gì?
Bandi-T

23
Thành thật mà nói không có mục đích nào cả! Tôi chỉ muốn tìm hiểu phải làm gì khi bạn cần giao diện trong python?
Pratik Deoghare

18
raise NotImplementedErrorlà những gì show's cơ thể nên được - làm cho không có ý nghĩa để nâng cao một hoàn toàn chung Exceptionkhi Python định nghĩa một cụ thể một cách hoàn hảo được xây dựng trong một -!)
Alex Martelli

2
Không nên init gọi show () trong IInterface (hoặc nâng cao ngoại lệ riêng của mình) nên bạn không thể khởi tạo một giao diện trừu tượng?
Chuyến đi Katastic

1
Tôi có thể thấy một số sử dụng cho việc này ... giả sử bạn có một đối tượng mà bạn muốn đảm bảo có chữ ký cụ thể. Với cách gõ vịt, bạn không thể đảm bảo rằng đối tượng sẽ có chữ ký mà bạn mong đợi. Đôi khi nó có thể hữu ích để thực thi một số cách gõ trên các thuộc tính được gõ động.
Prime By Design

Câu trả lời:


150

Như được đề cập bởi khác ở đây:

Các giao diện không cần thiết trong Python. Điều này là do Python có nhiều kế thừa thích hợp và cả vịt con, điều đó có nghĩa là những nơi bạn phải có giao diện trong Java, bạn không cần phải có chúng trong Python.

Điều đó nói rằng, vẫn còn một số sử dụng cho giao diện. Một số trong số chúng được bao phủ bởi các lớp cơ sở trừu tượng của Pythons, được giới thiệu trong Python 2.6. Chúng rất hữu ích, nếu bạn muốn tạo các lớp cơ sở không thể khởi tạo được, nhưng cung cấp một giao diện cụ thể hoặc một phần của việc triển khai.

Một cách sử dụng khác là nếu bạn bằng cách nào đó muốn xác định rằng một đối tượng thực hiện một giao diện cụ thể và bạn cũng có thể sử dụng ABC cho điều đó bằng cách phân lớp từ chúng. Một cách khác là zope.interface, một mô-đun là một phần của Kiến trúc thành phần Zope, một khung thành phần thực sự tuyệt vời. Ở đây bạn không phân lớp từ các giao diện, mà thay vào đó đánh dấu các lớp (hoặc thậm chí là các thể hiện) là thực hiện giao diện. Điều này cũng có thể được sử dụng để tra cứu các thành phần từ sổ đăng ký thành phần. Siêu mát!


11
Bạn có thể giải thích về điều này? 1. Làm thế nào để thực hiện một giao diện như vậy? 2. Làm thế nào nó có thể được sử dụng để tra cứu các thành phần?
địa chất

42
"Các giao diện không cần thiết trong Python. Ngoại trừ khi chúng có."
Baptiste Candello

8
giao diện chủ yếu được sử dụng để có kết quả dự đoán / thực thi tính chính xác của các thành viên khi chuyển các đối tượng xung quanh. Sẽ thật tuyệt nếu python hỗ trợ điều này như một tùy chọn. nó cũng sẽ cho phép các công cụ phát triển có khả năng giao tiếp tốt hơn
Sonic Soul

1
Một ví dụ sẽ cải thiện đáng kể câu trả lời này.
bob

5
"Điều này là do Python có nhiều kế thừa phù hợp", ai nói giao diện là dành cho nhiều kế thừa?
adnanmuttaleb

76

Sử dụng mô-đun abc cho các lớp cơ sở trừu tượng dường như thực hiện thủ thuật.

from abc import ABCMeta, abstractmethod

class IInterface:
    __metaclass__ = ABCMeta

    @classmethod
    def version(self): return "1.0"
    @abstractmethod
    def show(self): raise NotImplementedError

class MyServer(IInterface):
    def show(self):
        print 'Hello, World 2!'

class MyBadServer(object):
    def show(self):
        print 'Damn you, world!'


class MyClient(object):

    def __init__(self, server):
        if not isinstance(server, IInterface): raise Exception('Bad interface')
        if not IInterface.version() == '1.0': raise Exception('Bad revision')

        self._server = server


    def client_show(self):
        self._server.show()


# This call will fail with an exception
try:
    x = MyClient(MyBadServer)
except Exception as exc:
    print 'Failed as it should!'

# This will pass with glory
MyClient(MyServer()).client_show()

11
Yugghh, cần một số mô-đun cho những gì nên là một phần của ngôn ngữ, hoặc hoàn toàn không được sử dụng, IMO.
Mike de Klerk

ý bạn là if not server.version() == '1.0': raise ...sao? Tôi thực sự không có được dòng này. Một lời giải thích sẽ được hoan nghênh.
Skandix

1
@MikedeKlerk Tôi không thể đồng ý nhiều hơn. Giống như câu trả lời của Python để gõ; Tôi không nên nhập một mô-đun để khai báo Tôi muốn một loại là một loại. Câu trả lời cho điều đó thường là "Python tốt được gõ động", nhưng đó không phải là lý do. Java + Groovy giải quyết vấn đề này. Java cho các công cụ tĩnh, Groovy cho các công cụ động.
ubiquibacon

6
@MikedeKlerk, mô-đun abc thực sự được tích hợp sẵn cho python. Việc thiết lập một số các mẫu này sẽ tốn công hơn một chút vì chúng hầu như không cần thiết trong Python do các mẫu thay thế được coi là 'nhiều Pythonic' hơn. Đối với đại đa số các nhà phát triển, nó sẽ như bạn nói, 'hoàn toàn không được sử dụng'. Tuy nhiên, thừa nhận rằng có một số trường hợp rất thực sự đòi hỏi các khả năng giao thoa này, những người tạo Python đã cung cấp API dễ sử dụng để biến điều đó thành có thể.
David Culbreth

39

giao diện hỗ trợ Python 2.7 và Python 3.4+.

Để cài đặt giao diện, bạn phải

pip install python-interface

Mã ví dụ:

from interface import implements, Interface

class MyInterface(Interface):

    def method1(self, x):
        pass

    def method2(self, x, y):
        pass


class MyClass(implements(MyInterface)):

    def method1(self, x):
        return x * 2

    def method2(self, x, y):
        return x + y

7
Một lợi thế lớn của thư viện này, IMHO, là sự thất bại sớm mà nó mang lại cho bạn: Nếu lớp của bạn không thực hiện đúng giao diện đã chỉ định, bạn sẽ có một ngoại lệ ngay khi lớp được đọc - bạn thậm chí không phải sử dụng nó . Với lớp cơ sở trừu tượng của Python, bạn sẽ có ngoại lệ khi lần đầu tiên khởi tạo lớp của mình, có thể muộn hơn nhiều.
Hans

Không cần thiết, ABC cung cấp chức năng tích hợp tương tự.
Daniel

@DanielCasares ABC có cung cấp giao diện thực tế hay bạn muốn nói rằng các lớp trừu tượng không có trạng thái hoặc triển khai là giải pháp mà ABC cung cấp?
asaf92

36

Việc triển khai các giao diện với các lớp cơ sở trừu tượng đơn giản hơn nhiều trong Python 3 hiện đại và chúng phục vụ mục đích như một hợp đồng giao diện cho các tiện ích mở rộng của trình cắm thêm.

Tạo giao diện / lớp cơ sở trừu tượng:

from abc import ABC, abstractmethod

class AccountingSystem(ABC):

    @abstractmethod
    def create_purchase_invoice(self, purchase):
        pass

    @abstractmethod
    def create_sale_invoice(self, sale):
        log.debug('Creating sale invoice', sale)

Tạo một lớp con bình thường và ghi đè tất cả các phương thức trừu tượng:

class GizmoAccountingSystem(AccountingSystem):

    def create_purchase_invoice(self, purchase):
        submit_to_gizmo_purchase_service(purchase)

    def create_sale_invoice(self, sale):
        super().create_sale_invoice(sale)
        submit_to_gizmo_sale_service(sale)

Bạn có thể tùy chọn có triển khai chung trong các phương thức trừu tượng như trong create_sale_invoice(), gọi nó super()một cách rõ ràng trong lớp con như trên.

Khởi tạo một lớp con không thực hiện tất cả các phương thức trừu tượng đều thất bại:

class IncompleteAccountingSystem(AccountingSystem):
    pass

>>> accounting = IncompleteAccountingSystem()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class IncompleteAccountingSystem with abstract methods
create_purchase_invoice, create_sale_invoice

Bạn cũng có thể có các thuộc tính trừu tượng, phương thức tĩnh và lớp bằng cách kết hợp các chú thích tương ứng với @abstractmethod.

Các lớp cơ sở trừu tượng là tuyệt vời để thực hiện các hệ thống dựa trên plugin. Tất cả các lớp con được nhập của một lớp đều có thể truy cập thông qua __subclasses__(), vì vậy nếu bạn tải tất cả các lớp từ thư mục plugin importlib.import_module()và nếu chúng phân lớp lớp cơ sở, bạn có quyền truy cập trực tiếp vào chúng thông qua __subclasses__()và bạn có thể chắc chắn rằng hợp đồng giao diện được thi hành cho tất cả chúng trong thời gian khởi tạo.

Đây là cách thực hiện tải plugin cho AccountingSystemví dụ trên:

...
from importlib import import_module

class AccountingSystem(ABC):

    ...
    _instance = None

    @classmethod
    def instance(cls):
        if not cls._instance:
            module_name = settings.ACCOUNTING_SYSTEM_MODULE_NAME
            import_module(module_name)
            subclasses = cls.__subclasses__()
            if len(subclasses) > 1:
                raise InvalidAccountingSystemError('More than one '
                        f'accounting module: {subclasses}')
            if not subclasses or module_name not in str(subclasses[0]):
                raise InvalidAccountingSystemError('Accounting module '
                        f'{module_name} does not exist or does not '
                        'subclass AccountingSystem')
            cls._instance = subclasses[0]()
        return cls._instance

Sau đó, bạn có thể truy cập đối tượng plugin hệ thống kế toán thông qua AccountingSystemlớp:

>>> accountingsystem = AccountingSystem.instance()

(Lấy cảm hứng từ bài đăng PyMOTW-3 này .)


Câu hỏi: Tên mô-đun "ABC" có nghĩa là gì?
Sebastian Nielsen

"ABC" là viết tắt của "Các lớp cơ sở trừu tượng", xem các tài liệu chính thức
mrts

31

Có các triển khai giao diện của bên thứ ba cho Python (phổ biến nhất là Zope , cũng được sử dụng trong Twisted ), nhưng thông thường hơn, các lập trình viên Python thích sử dụng khái niệm phong phú hơn được gọi là "Lớp cơ sở trừu tượng" (ABC), kết hợp giao diện với khả năng có một số khía cạnh thực hiện ở đó quá. Các ABC được hỗ trợ đặc biệt tốt trong Python 2.6 trở lên, hãy xem PEP , nhưng ngay cả trong các phiên bản trước của Python, chúng thường được xem là "con đường để đi" - chỉ cần định nghĩa một lớp mà một số phương thức của chúng sẽ nâng lên NotImplementedErrorđể các lớp con sẽ lưu ý rằng họ sẽ ghi đè tốt hơn các phương thức đó! -)


3
Có các triển khai giao diện của bên thứ ba cho Python Nó có nghĩa là gì? Bạn có thể vui lòng giải thích ABC?
Pratik Deoghare

2
Chà, tôi sẽ có vấn đề với việc ABC trở nên "giàu có" hơn. ;) Có những điều zope.interface có thể làm mà ABC không thể cũng như cách khác. Nhưng nếu không thì bạn vẫn như thường lệ. +1
Lennart Regebro

1
@Alfred: Có nghĩa là các mô-đun như zope.interface không được bao gồm trong thư viện chuẩn, nhưng có sẵn từ pypi.
Lennart Regebro

Tôi vẫn còn khó khăn để tìm hiểu khái niệm về ABC. Liệu ai đó có thể viết lại twistmatrix.com/document/civerse/core/howto/components.html (IMHO, một giải thích tuyệt vời về khái niệm giao diện) theo thuật ngữ của ABC. Liệu nó có ý nghĩa?
mcepl

21

Một cái gì đó như thế này (có thể không hoạt động vì tôi không có Python xung quanh):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"

2
Tôi nên làm gì về __init__(self)các nhà xây dựng?
Pratik Deoghare

1
Tùy bạn Vì không có kiểm tra thời gian biên dịch chống lại việc xây dựng một đối tượng từ một lớp trừu tượng, bạn sẽ không nhận được bất kỳ sự bảo vệ nào trong quá trình mã hóa / biên dịch. Sẽ có một hàm tạo được kế thừa, vì vậy đối tượng sẽ được tạo, chỉ là "trống rỗng". Tùy thuộc vào bạn quyết định liệu bạn có tốt hơn hay không bằng cách cho phép điều này xảy ra và gặp thất bại sau đó, hoặc dừng rõ ràng chương trình ngay lúc đó và bằng cách thực hiện một hàm tạo tương tự ném ngoại lệ.
Bandi-T

1
Đó là lý do tại sao abc.ABCtốt hơn nhiều so với việc nâng cao NotImplementedError- khởi tạo một abc.ABClớp con không thực hiện tất cả các phương thức trừu tượng bị lỗi sớm, vì vậy bạn được bảo vệ chống lại lỗi. Xem câu trả lời của tôi dưới đây để biết lỗi như thế nào.
mrts

8

Tôi hiểu rằng các giao diện không cần thiết trong các ngôn ngữ động như Python. Trong Java (hoặc C ++ với các lớp cơ sở trừu tượng) là các phương tiện để đảm bảo rằng ví dụ: bạn đang truyền đúng tham số, có thể thực hiện tập hợp các tác vụ.

Ví dụ: nếu bạn có người quan sát và có thể quan sát, có thể quan sát được việc đăng ký các đối tượng hỗ trợ giao diện IObserver, từ đó có notifyhành động. Điều này được kiểm tra tại thời gian biên dịch.

Trong Python, không có điều gì như compile timevà tra cứu phương thức được thực hiện trong thời gian chạy. Hơn nữa, người ta có thể ghi đè tra cứu bằng các phương thức ma thuật __getattr __ () hoặc __getattribution __ (). Nói cách khác, bạn có thể vượt qua, với tư cách là người quan sát, bất kỳ đối tượng nào có thể trả về có thể gọi được khi truy cập notifythuộc tính.

Điều này dẫn tôi đến kết luận rằng các giao diện trong Python tồn tại - chỉ là việc thực thi của chúng bị hoãn lại cho đến thời điểm chúng thực sự được sử dụng

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.