Python'da arayüzleri nasıl uygularım?


182
public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

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

    #endregion
}

Bu C # kodunun Python eşdeğerini nasıl uygulayabilirim?

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

Bu iyi bir fikir mi ?? Lütfen yanıtlarınızda örnekler verin.


Sizin durumunuzda bir arayüz kullanmanın amacı ne olabilir?
Bandi-T

23
Açıkçası hiç bir amacı yok! Python'da arayüzlere ihtiyacınız olduğunda ne yapacağınızı öğrenmek istiyorum?
Pratik Deoghare

18
raise NotImplementedErrorne showde üstü olmalıdır - Tamamen jenerik yükseltmek için hiçbir mantıklı ExceptionPython mükemmel özgü tanımlar zaman yerleşik one -!)
Alex Martelli

2
Olmamalı init Iinterface çağrı gösterisi () (veya istisna kendisi yükseltmek) Eğer soyut bir arayüz örneğini olamaz yani?
Katastic Voyage

1
Bunun bazı kullanımlarını görebiliyorum ... diyelim ki belirli bir imzası olduğundan emin olmak istediğiniz bir nesneniz var. Ördek yazarak, nesnenin beklediğiniz imzanın olacağını garanti edemezsiniz. Bazen dinamik olarak yazılan özellikler üzerinde bazı yazımları uygulamak yararlı olabilir.
Prime By Design

Yanıtlar:


151

Burada başkaları tarafından belirtildiği gibi:

Python'da arayüzler gerekli değildir. Bunun nedeni, Python'un uygun çoklu mirasa ve aynı zamanda ducktyping'e sahip olmasıdır, bu da Java'da arayüzlere sahip olmanız gereken yerlerin Python'da olması gerekmediği anlamına gelir .

Bununla birlikte, arayüzler için hala birkaç kullanım alanı vardır. Bazıları Python 2.6'da tanıtılan Pythons Abstract Base Classes kapsamındadır. Örneklenemeyecek temel sınıflar yapmak, ancak belirli bir arabirim veya uygulamanın bir parçası sağlamak istiyorsanız bunlar yararlıdır.

Başka bir kullanım, bir şekilde bir nesnenin belirli bir arabirimi uyguladığını belirtmek istiyorsanız ve bunun için ABC'yi de onlardan alt sınıflar kullanarak kullanabilirsiniz. Başka bir yol da, gerçekten harika bir bileşen çerçevesi olan Zope Bileşen Mimarisinin bir parçası olan zope.interface'dir. Burada arabirimlerden alt sınıf oluşturmazsınız, bunun yerine sınıfları (hatta örnekleri) bir arabirimi uygularken işaretleyin. Bu, bileşen kaydından bileşenleri aramak için de kullanılabilir. Süper havalı!


11
Bu konuda ayrıntılı misiniz? 1. Böyle bir arayüz nasıl uygulanır? 2. Bileşenleri aramak için nasıl kullanılabilir?
geoidesic

43
"Python'da arabirimler gerekli değildir. Ne zaman oldukları dışında."
Baptiste Candellier

8
arayüzler çoğunlukla nesnelerin etrafından geçerken üyelerin öngörülebilir bir sonucuna / doğruluğuna sahip olmak için kullanılır. python bunu bir seçenek olarak destekleseydi harika olurdu. aynı zamanda geliştirme araçlarının daha iyi anlaşılırlığa sahip olmasına izin verir
Sonic Soul

1
Bir örnek bu cevabı büyük ölçüde geliştirecektir.
bob

5
"Bunun nedeni Python'un uygun çoklu mirasa sahip olmasıdır", arayüzlerin çoklu miras olduğunu kim söyledi?
adnanmuttaleb

76

Soyut temel sınıflar için abc modülünü kullanmak hile yapıyor gibi görünüyor.

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, dilin kendisinin bir parçası olması gereken ya da hiç kullanılmayan IMO için bir modüle ihtiyaç duyuyor.
Mike de Klerk

Şunu mu demek istedin if not server.version() == '1.0': raise ...? Bu çizgiyi gerçekten anlamıyorum. Bir açıklama memnuniyetle karşılanacaktır.
Skandix

1
@MikedeKlerk Daha fazla katılamadım. Tıpkı Python'un yazmaya cevabı gibi; Bir türün bir tür olmasını istediğimi bildirmek için bir modül almak zorunda olmamalıyım. Buna verilen yanıt genellikle "iyi Python dinamik olarak yazılmıştır", ancak bu bir bahane değildir. Java + Groovy bu sorunu çözer. Statik şeyler için Java, dinamik şeyler için Groovy.
ubiquibacon

6
@MikedeKlerk, abc modülü aslında python'da yerleşiktir. Bu kalıplardan bazılarını oluşturmak biraz daha fazla iştir, çünkü 'daha Pythonic' olarak kabul edilen alternatif kalıplar nedeniyle Python'da büyük ölçüde gereksizdirler. Geliştiricilerin büyük çoğunluğu için, dediğin gibi olurdu, 'hiç kullanılmaz'. Bununla birlikte, bu arayüz yeteneklerini gerçekten gerektiren bazı çok niş vakalar olduğunu kabul eden Python yaratıcıları bunu mümkün kılmak için kullanımı kolay bir API sağladı.
David Culbreth

39

arayüzü Python 2.7 ve Python 3.4+ destekler.

To yüklemek Mecbur arayüz

pip install python-interface

Örnek Kod:

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
Bu kütüphanenin önemli bir avantajı olan IMHO, size verdiği erken başarısızlıktır: Sınıfınız belirli bir arabirimi doğru bir şekilde uygulamıyorsa, sınıf okunduğunda bir istisna alırsınız - onu kullanmanız bile gerekmez . Python'un kendi soyut temel sınıfıyla, sınıfınızı ilk kez başlattığınızda istisna elde edersiniz, ki bu daha sonra olabilir.
Hans

Gereksizdir, ABC benzer, yerleşik bir işlevsellik sunar.
Daniel

@DanielCasares ABC gerçek bir arayüz sunuyor mu yoksa devlet veya uygulama içermeyen soyut sınıfların ABC'nin sunduğu çözüm olduğunu mu kastediyorsunuz?
asaf92

36

Soyut temel sınıflarla arayüzler uygulamak modern Python 3'te çok daha basittir ve eklenti uzantıları için bir arayüz sözleşmesi olarak bir amaca hizmet ederler.

Interface / abstract base sınıfını oluşturun:

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)

Normal bir alt sınıf oluşturun ve tüm soyut yöntemleri geçersiz kılın:

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)

İsteğe bağlı olarak, yukarıdaki gibi alt sınıfta açıkça create_sale_invoice()çağırarak soyut yöntemlerde olduğu gibi ortak uygulamaya sahip olabilirsiniz super().

Tüm soyut yöntemleri uygulamayan bir alt sınıfın oluşturulması başarısız olur:

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

İlgili ek açıklamaları bir araya getirerek soyut özelliklere, statik ve sınıf yöntemlerine de sahip olabilirsiniz @abstractmethod.

Soyut temel sınıflar eklenti tabanlı sistemleri uygulamak için mükemmeldir. Bir sınıfın tüm içe aktarılan alt sınıflarına şu yolla erişilebilir __subclasses__(); bu nedenle, tüm sınıfları bir eklenti dizininden importlib.import_module()yüklerseniz ve temel sınıfın alt sınıflarına girerseniz, bunlara doğrudan erişebilirsiniz __subclasses__()ve arabirim sözleşmesinin tümü için uygulandığından emin olabilirsiniz. onları örnekleme sırasında.

AccountingSystemYukarıdaki örnek için eklenti yükleme uygulaması şöyledir:

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

Daha sonra muhasebe sistemi eklentisi nesnesine AccountingSystemsınıf üzerinden erişebilirsiniz :

>>> accountingsystem = AccountingSystem.instance()

( Bu PyMOTW-3 yayınından esinlenilmiştir .)


Soru: "ABC" modül adı ne anlama geliyor?
Sebastian Nielsen

"ABC", "Soyut Temel Sınıflar" anlamına gelir, bkz. Resmi dokümanlar
mrts

31

Python için arayüzlerin üçüncü taraf uygulamaları vardır (en popüler olanı, Twisted'da da kullanılan Zope'dir ), ancak daha yaygın olarak Python kodlayıcıları, bir arayüzü bir araya getiren "Abstract Base Class" (ABC) olarak bilinen daha zengin konsepti kullanmayı tercih eder. orada da bazı uygulama yönlerine sahip olma olasılığı. ABC'si özellikle Python 2.6 desteklenen ve daha sonra, bkz edilir PEP ancak onlar bile normalde "gitmek için yol" olarak görülen konum Python önceki sürümlerinde, - sadece kimin yöntemler bazı yükseltmek bir sınıf tanımlamak NotImplementedErroralt sınıfları olacak şekilde bu yöntemleri geçersiz kılmaları daha iyi olur! -)


3
Python için arayüzlerin üçüncü taraf uygulamaları var Ne anlama geliyor? ABC'yi açıklayabilir misiniz?
Pratik Deoghare

2
ABC'nin "daha zengin" olması sorununu ele alacağım. ;) Zope.interface'in ABC'nin yapamayacağı diğer şeylerin yapabileceği şeyler vardır. Ama aksi halde her zamanki gibi haklısın. +1
Lennart Regebro

1
@Alfred: Bu, zope.interface gibi modüllerin standart kütüphaneye dahil olmadığı, ancak pypi'den sağlandığı anlamına gelir.
Lennart Regebro

ABC kavramını incelemek için hala zor zamanlarım var. Birisi için ABC'ler açısından twistedmatrix.com/documents/current/core/howto/components.html (IMHO, arayüzler konseptinin mükemmel bir açıklaması) yeniden yazılabilir mi ? Bir anlam ifade ediyor mu?
mcepl

21

Böyle bir şey (etrafında Python yok gibi çalışmayabilir):

class IInterface:
    def show(self): raise NotImplementedError

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

2
__init__(self)Yapıcı hakkında ne yapmalıyım ?
Pratik Deoghare

1
Sana bağlı. Soyut bir sınıftan bir nesne oluşturmaya karşı derleme zamanı kontrolü olmadığından, kodlama / derleme sırasında herhangi bir koruma elde edemezsiniz. Nesne bu yüzden, kalıtsal bir yapıcı olacaktır olurdu oluşturulan olsun, sadece "boş" olarak. Bunun olmasına izin vererek daha iyi olup olmayacağınıza karar vermek ve daha sonra hataları yakalamak veya bir istisna atarak benzer bir kurucu uygulayarak programı hemen o anda ve orada durdurmak için size kalmış.
Bandi-T

1
Bu yüzden abc.ABCyükseltmekten çok daha iyidir NotImplementedError- abc.ABCtüm soyut yöntemleri uygulamayan bir alt sınıfın başlatılması erken başarısız olur, bu nedenle hatalara karşı korunursunuz. Hatanın nasıl göründüğüne ilişkin aşağıdaki cevabıma bakın.
mrts

8

Anladığım kadarıyla, arayüzler Python gibi dinamik dillerde gerekli değildir. Java (veya soyut temel sınıfıyla C ++) arayüzleri, örneğin doğru parametreyi geçtiğinizi, görevler kümesini gerçekleştirebilmenizi sağlayan araçlardır.

Gözlemciniz ve gözlemlenebilir durumunuz varsa, gözlemlenebilir olan IObserver arayüzünü destekleyen nesnelere abone olmakla ilgilenir. notify eylem içerir. Bu derleme zamanında kontrol edilir.

Python'da böyle bir şey yoktur compile timeve yöntem aramaları çalışma zamanında gerçekleştirilir. Ayrıca, __getattr __ () veya __getattribute __ () sihirli yöntemleriyle aramayı geçersiz kılabilir. Başka bir deyişle, gözlemci olarak, erişilirken çağrılabilir olarak geri dönebilen herhangi bir nesneyi iletebilirsiniz.notify özniteliğe .

Sonuca Bu potansiyel müşteriler beni, Python arabirimler bu mevcut olamayacağı - bu sadece onların icra aslında kullanıldıkları an ertelendi mi

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.