Frage

Weil ich in Python zu dem alten Wegen der Ente Typisierung verwendet werde, ich nicht die Notwendigkeit für ABC (abstrakte Basisklassen) zu verstehen. Die Hilfe ist gut, wie sie verwenden.

Ich habe versucht, die Gründe in dem PEP zu lesen, aber es ging über meinen Kopf. Wenn ich für einen wandelbaren Sequenz-Container suchen, würde ich für __setitem__ überprüfen, oder eher versuchen, es zu verwenden ( EAFP ). Ich bin nicht gekommen, über eine wirkliches Leben Verwendung für die Zahlen Modul, die Verwendung ABCs der Fall ist, sondern dass die nächste, die ich zu verstehen haben, ist.

Kann jemand erklären, die Gründe für mich, bitte?

War es hilfreich?

Lösung

Kurzversion

ABCs bietet ein höheres Maß an semantischem Vertrag zwischen Kunden und den implementierten Klassen.

Long Version

Es ist ein Vertrag zwischen einer Klasse und ihren Anrufern. Die Klasse verspricht, bestimmte Dinge zu tun, und bestimmte Eigenschaften haben.

Es gibt verschiedene Stufen des Auftrags.

Bei einem sehr niedrigen Niveau, könnte der Vertrag umfasst den Namen einer Methode oder seine Anzahl von Parametern.

In einer staticly typisierte Sprache, würde dieser Vertrag tatsächlich vom Compiler erzwungen werden. In Python können Sie EAFP oder Selbstbeobachtung zu bestätigen verwenden, dass das unbekannte Objekt diesen erwarteten Vertrag erfüllt.

Es gibt aber auch auf höhere Ebene, semantische Versprechen im Vertrag.

Zum Beispiel, wenn es eine __str__() Methode ist, wird erwartet, eine String-Darstellung des Objekts zurückzukehren. Es könnte alle Inhalte des Objekts löschen, die Transaktion fest und eine leere Seite aus dem Drucker spuckt ... aber es gibt ein gemeinsames Verständnis von dem, was sie tun sollen, in dem Python-Handbuch beschrieben.

Das ist ein Sonderfall, in dem der semantischen Vertrag im Handbuch beschrieben wird. Was tun soll, die print() Methode? Sollte es das Objekt an einen Drucker oder eine Leitung mit dem Bildschirm, oder etwas anderes schreiben? Es hängt davon ab - Sie können die Kommentare lesen müssen hier den vollständigen Vertrag zu verstehen. Ein Stück Client-Code, dass einfach überprüft, dass die print() Methode existiert Bestandteil des Vertrages bestätigt hat -., Dass ein Methodenaufruf gemacht werden kann, aber nicht, dass es eine Einigung über die höhere Ebene Semantik des Anrufs

Definition eine abstrakte Basisklasse (ABC) ist eine Möglichkeit, einen Vertrag zwischen der Klassen Implementierer zu erzeugen und den Anrufern. Es ist nicht nur eine Liste von Methodennamen, sondern ein gemeinsames Verständnis von dem, was diese Methoden tun sollen. Wenn Sie von diesem ABC erben, Sie sind viel versprechend, alle Regeln in den Kommentaren beschrieben zu folgen, einschließlich der Semantik des print() Methode.

Pythons duck-Typisierung hat viele Vorteile in der Flexibilität gegenüber statischen Typisierung, aber es löst nicht alle Probleme. ABCs bietet eine Zwischenlösung zwischen der freien Form von Python und der Fessel-und-Disziplin eines staticly typisierte Sprache.

Andere Tipps

@ Oddthinking Antwort ist nicht falsch, aber ich denke, es fehlt in der real praktische Grund Python hat ABCs in einer Welt von Enten Typisierung.

Abstrakte Methoden sind ordentlich, aber meiner Meinung nach sie füllen nicht wirklich alle Anwendungsfälle nicht bereits von Duck Typing abgedeckt. Abstrakte Basisklassen wirkliche Macht liegt in die Art, wie sie erlauben Sie anpassen das Verhalten von isinstance und issubclass . (__subclasshook__ ist im Grunde ein freundlicher API für Python __instancecheck__ und __subclasscheck__ Haken.) Anpassung Einbau-Konstrukte zur Arbeit auf benutzerdefinierte Typen sehr viel Philosophie Teil von Python ist.

Python Quellcode ist beispielhaft. Hier ist, wie collections.Container in der Standardbibliothek definiert ( zum Zeitpunkt des Schreibens):

class Container(metaclass=ABCMeta):
    __slots__ = ()

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if any("__contains__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

Diese Definition von __subclasshook__ besagt, dass jede Klasse mit einem __contains__ Attribute wird als eine Unterklasse von Containern sein, auch wenn sie es nicht direkt Unterklasse. Also habe ich diese schreiben kann:

class ContainAllTheThings(object):
    def __contains__(self, item):
        return True

>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True

Mit anderen Worten: , wenn Sie die richtige Schnittstelle implementieren, sind Sie eine Unterklasse! ABCs bietet eine formale Art und Weise Schnittstellen in Python zu definieren, während treu zu bleiben den Geist der Ente-Typisierung. Außerdem funktioniert dies in einer Weise, dass die Auszeichnung Frei Geschlossen-Prinzip .

Pythons Objektmodell sieht oberflächlich ähnlich dem von einer „traditionellen“ OO-System (und damit meine ich Java *) - wir yer Klassen bekam, yer Objekte, yer Methoden - aber wenn man an der Oberfläche kratzen werden Sie feststellen, etwas viel reicher und flexibler. Ebenso Python Begriff der abstrakten Basisklassen zu einem Java-Entwickler können erkennbar sein, aber in der Praxis sind sie für einen ganz anderen Zweck bestimmt sind.

Ich finde manchmal schreibe ich polymorphe Funktionen, die auf ein einzelnes Element oder eine Sammlung von Gegenständen handeln können, und ich isinstance(x, collections.Iterable) finden viel besser lesbar als hasattr(x, '__iter__') oder einem gleichwertigen try...except Block werden. (Wenn Sie nicht wissen, Python, die von den dreien würde die Absicht des Codes klarste machen?)

sagte, finde ich, dass muß ich selten mein eigenes ABC schreiben, und ich entdecke typischerweise die Notwendigkeit für einen durch Refactoring. Wenn ich eine polymorphe Funktion eine Menge von Attributprüfungen machen, oder viele Funktionen finden Sie in den gleichen Attributprüfungen machen, dass Geruch schlägt die Existenz eines ABC extrahiert zu warten.

* ohne in die Debatte immer darüber, ob Java ist ein "traditionelles" OO-System ...


Nachtrag : Obwohl eine abstrakte Basisklasse, das Verhalten von isinstance und issubclass außer Kraft setzen kann, ist es noch nicht in den MRO der virtuellen Unterklasse. Dies ist eine potenzielle Gefahr für die Kunden. Nicht jedes Objekt, für das isinstance(x, MyABC) == True die Methoden auf MyABC definiert hat

class MyABC(metaclass=abc.ABCMeta):
    def abc_method(self):
        pass
    @classmethod
    def __subclasshook__(cls, C):
        return True

class C(object):
    pass

# typical client code
c = C()
if isinstance(c, MyABC):  # will be true
    c.abc_method()  # raises AttributeError

Leider ist dieser einer von denen „einfach nicht tun, dass“ Fallen (von denen Python relativ wenige hat!): Vermeiden Sie definieren ABCs sowohl mit einem __subclasshook__ und nicht-abstrakten Methoden. Darüber hinaus sollten Sie Ihre Definition von __subclasshook__ im Einklang mit der Reihe von abstrakten Methoden, um Ihr ABC definiert werden.

Ein praktisches Feature von ABCs ist, dass, wenn Sie nicht alle erforderlichen Methoden implementieren (und Objekte) Sie einen Fehler bei der Instanziierung bekommen, anstatt ein AttributeError , möglicherweise viel später, wenn Sie tatsächlich versuchen, die fehlende Methode zu verwenden.

from abc import ABCMeta, abstractmethod

# python2
class Base(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

# python3
class Base(object, metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

class Concrete(Base):
    def foo(self):
        pass

    # We forget to declare `bar`


c = Concrete()
# TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"

Beispiel von https://dbader.org/blog/abstract-base- Klassen-in-python

Edit: schließen python3 Syntax dank @PandasRocks

Es wird die Bestimmung, ob ein Objekt ein bestimmtes Protokoll unterstützt, ohne Anwesenheit aller Verfahren, die in dem Protokoll zu überprüfen, mit oder ohne Auslösen eine Ausnahme tief in „Feind“ Territorium wegen Nicht Unterstützung viel einfacher.

Abstrakte Methode stellen Sie sicher, dass das, was Methode Sie in der übergeordneten Klasse aufrufen hat in der Kinderklasse zu sein scheinen. Im Folgenden sind noraml Weise abstrakt aufrufen und verwenden. Das Programm geschrieben in python3

Normalweg des Aufrufs

class Parent:
def methodone(self):
    raise NotImplemented()

def methodtwo(self):
    raise NotImplementedError()

class Son(Parent):
   def methodone(self):
       return 'methodone() is called'

c = Son()
c.methodone()
  
    

'Methodon () aufgerufen wird'

  
c.methodtwo()
  
    

NotImplementedError

  

Mit Abstraktem Methode

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'

c = Son()
  
    

Typeerror:. Kann nicht instantiate abstrakte Klasse Son mit abstrakten Methoden methodtwo

  

Da methodtwo nicht in der Kinderklasse genannt haben wir Fehler. Die ordnungsgemäße Umsetzung ist unter

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'
    def methodtwo(self):
        return 'methodtwo() is called'

c = Son()
c.methodone()
  
    

'Methodon () aufgerufen wird'

  
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top