[GO] ABC für Python-Abstract-Klassen und Ententypisierung

Dieser Artikel ist der Artikel zum 8. Tag von Python Advent Calendar 2015.


Normalerweise schreibe ich Go und ehrlich gesagt schreibe ich selten Python. Ich denke darüber nach, es in der Mathematik zu verwenden, also bin ich dieses Mal in den Adventskalender von Python gesprungen.

In Go ist es jetzt wichtig, eine Schnittstelle zu definieren und in einem abstrahierten Muster für eine groß angelegte Entwicklung zu implementieren. Hier geht es nicht nur um Go, die Objekte werden abstrahiert, und es wird ein großartiger Code, der gut lesbar ist und bei bescheidener Verwendung geringe Abhängigkeiten aufweist.

In der Zwischenzeit habe ich untersucht, welche Methode für die Abstraktion in Python verfügbar ist. Verwenden Sie das Modul, das in der Sprachspezifikation nicht vorhanden ist und als Modul mit dem Namen ABC (Abstract Base Class) bereitgestellt wird.

ABC - Modul für abstrakte Basisklassen

In Python können abstrakte Klassen mithilfe des ABC-Moduls (Abstract Base Class) (https://docs.python.org/3.5/library/abc.html) implementiert werden. Die abstrakte Basisklasse kann durch eine Metaklasse namens ABCMeta definiert werden, und die definierte abstrakte Basisklasse kann als Oberklasse zum Definieren von Unterklassen verwendet werden.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from abc import ABCMeta, abstractmethod

#Abstrakte Klasse
class Animal(metaclass=ABCMeta):
    @abstractmethod
    def sound(self):
        pass

#Abstrakte Klasse erben
class Cat(Animal):
    def sound(self):
        print("Meow")

if __name__ == "__main__":
    assert issubclass(Cat().__class__, Animal)
    assert isinstance(Cat(), Animal)

Wir haben eine abstrakte Klasse namens "Animal" definiert und die "Cat" -Klasse implementiert, die sie geerbt hat. Da es vererbt wird, scheint es natürlich, dass "issubclass" und "isinstance" durchlaufen werden.

Wenn die geerbte "Cat" -Klasse den "Sound" der abstrakten Methode nicht implementiert (@abstractmethod wird später beschrieben), tritt ein Laufzeitfehler auf, wie unten gezeigt. (* Beim Erstellen einer Instanz)

class Cat(Animal):
    pass

# TypeError: Can't instantiate abstract class Cat with abstract methods sound

Registrierung der virtuellen Unterklasse mit der Methode "register"

Anstatt Unterklassen zu definieren, ist es andererseits möglich, nicht verwandte Klassen so zu registrieren, dass sie sich wie abstrakte Klassen verhalten. Dies wird als ** virtuelle Unterklasse ** bezeichnet.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from abc import ABCMeta, abstractmethod

class Animal(metaclass=ABCMeta):
    @abstractmethod
    def sound(self):
        pass

#Erbt die abstrakte Klasse nicht,`sound`Methode implementieren
class Dog():
    def sound(self):
        print("Bow")

#Registrieren Sie Hund zu Tier der abstrakten Klasse
Animal.register(Dog)

if __name__ == "__main__":
    assert issubclass(Dog().__class__, Animal)
    assert isinstance(Dog(), Animal)

Wenn Sie sich bei einer virtuellen Unterklasse registrieren, ist diese Klasse die einer abstrakten Klasse. Wenn die abstrakte Methode jedoch nicht implementiert ist, tritt ein Laufzeitfehler auf, wie unten gezeigt. (* Beim Aufrufen einer Methode)

class Dog():
    pass

# AttributeError: 'Dog' object has no attribute 'sound'

Dekorateur der abstrakten Methode

@abstractmethod

Ein Dekorateur, der eine abstrakte Methode zeigt. Obwohl es sich um eine abstrakte Methode handelt, ist es auch möglich, den Prozess in einer Methode zu beschreiben, die einen Dekorateur angibt und aus einer Unterklasse aufruft.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from abc import ABCMeta, abstractmethod

class Animal(metaclass=ABCMeta):
    @abstractmethod
    def sound(self):
        print("Hello")

#Abstrakte Klasse erben
class Cat(Animal):
    def sound(self):
        #Rufen Sie den Sound der Vererbungsquelle auf
        super(Cat, self).sound()
        print("Meow")

if __name__ == "__main__":
    print(Cat().sound())

Sie können die geerbte abstrakte Methode mit "super (Cat, self) .sound ()" aufrufen. Es ist ein bisschen anders als Java.

@abstractclassmethod (version 3.2)

Es ist ein Dekorator der abstrakten Klassenmethode, wird jedoch ab Version 3.3 von @ classmethod wie folgt beschrieben.

class Animal(metaclass=ABCMeta):
    @classmethod
    @abstractmethod
    def sound_classmethod(self):
        pass

@abstractstaticmethod (version 3.2)

Es ist ein Dekorateur einer abstrakten statischen Methode, aber ab Version 3.3 wird es durch "@ staticmethod" wie folgt beschrieben.

class Animal(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def sound_staticmethod(self):
        pass

Ente tippen

Das Obige reicht für abstrakte Klassen aus, aber ich wollte danach [Duck Typing](https://ja.wikipedia.org/wiki/Duck Typing) überprüfen, damit ich es implementieren kann. ..

Was tippt Ente?

„Wenn es wie eine Ente läuft und wie eine Ente quakt, muss es eine Ente sein." - „Wenn Sie wie eine Ente gehen und weinen, ist es eine Ente."

Es ist wie ein Name.

Vereinfachen wir dies und sagen: "Wenn Sie weinen, ist es ein Tier." Und wenn Sie es zwangsweise in die Programmierung einfügen: "Wenn Sie eine Methode namens" Weinen "für ein Objekt implementieren, ist die konkrete Klasse" Tier "." Es ist zum ersten Mal schwer zu verstehen.

Implementierung

Es ist schneller zu sehen als in deinem Kopf zu denken.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from abc import ABCMeta, abstractmethod

class Animal(metaclass=ABCMeta):
    @abstractmethod
    def sound(self):
        pass

class Cat(Animal):
    def sound(self):
        print("Meow")

class Dog():
    def sound(self):
        print("Bow")

class Book():
    pass

Animal.register(Dog)

def output(animal):
    print(animal.__class__.__name__, end=": ")
    animal.sound()

if __name__ == "__main__":
    c = Cat()
    output(c)

    d = Dog()
    output(d)

    b = Book()
    output(b)

Wenn Sie like implementieren und ausführen, erhalten Sie das folgende Ergebnis und den folgenden Laufzeitfehler.

Cat: Meow
Dog: Bow
AttributeError: 'Book' object has no attribute 'sound'

"Buch" ohne "Ton" beim Entenschreiben ist in Ordnung, da es nicht "kein Ton = Tier" ist. Ich möchte den Fehler jedoch nach Möglichkeit erkennen. Ich kann das Gefühl nicht leugnen, dass dies dynamisches Tippen ist, aber ich möchte es nach Möglichkeit vermeiden, also werde ich Folgendes tun.

Vorschlag 1. "versuchen" ~ "außer"

def output(animal):
    print(animal.__class__.__name__, end=": ")
    try:
        animal.sound()
    except AttributeError:
        print('No sound')

Es ist eine Methode zum Abfangen, wenn eine Ausnahme mit "try" ausgespuckt wird. Da es sich jedoch um eine Ausführungsbasis handelt, kann sie erst behandelt werden, nachdem sie tatsächlich zu einer Ausnahme geworden ist. Ich kann es nicht sehr empfehlen.

Vorschlag 2. "hasattr"

def output(animal):
    if not hasattr(animal, 'sound'):
        return
    print(animal.__class__.__name__, end=": ")
    animal.sound()

Überprüft, ob das Attribut vorhanden ist. Nun, es ist vernünftig.

Vorschlag 3. "ist Instanz"

def output(animal):
    if not isinstance(animal, Animal):
        return
    print(animal.__class__.__name__, end=": ")
    animal.sound()

Bestimmt, ob die Klasse durch "isinstance" angegeben wird. Auf diese Weise können Sie die beabsichtigte abstrakte Klasse verarbeiten.

mypy

Es gab auch eine statische Tippprüfung mypy. Sie können sicher sein, wenn Sie an statisches Tippen gewöhnt sind.

From Python...

def fib(n):
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a+b

...to statically typed Python

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a+b

Aber dynamisch typisierte Sprachen haben sich damit etabliert, so dass es sich subtil anfühlt, es noch statischer zu machen.

abschließend

Es ist schön, abstrakte Klassen in Python implementieren zu können, aber ich kann nicht sagen, wann ein Fehler auftreten wird. Die Abstraktion ist interessant, da die Spezifikationen in jeder Sprache festgelegt sind, die Konzepte jedoch geringfügig voneinander abweichen. Ich fühle, dass ich die Eigenschaften der Sprache fühle.

Recommended Posts

ABC für Python-Abstract-Klassen und Ententypisierung
Informationen zu Python-Objekten und -Klassen
[Python] Über Executor und zukünftige Klassen
Ich habe eine Klasse in Python erstellt und versucht, Enten zu tippen
Python-Eingabe
Python-Klassen und -Instanzen, Instanzmethoden
Binden Sie Methoden an Python-Klassen und -Instanzen
Apropos alte und neue Klassen in Python
Dynamische Eingabe von Python
[Einführung in Python3 Tag 12] Kapitel 6 Objekte und Klassen (6.3-6.15)
Wie Python-Klassen und magische Methoden funktionieren.
[Einführung in Python3 Tag 11] Kapitel 6 Objekte und Klassen (6.1-6.2)
[Hikari-Python] Kapitel 09-02 Klassen (Erstellen und Instanziieren von Klassen)
[Python] Komprimieren und dekomprimieren
Python- und Numpy-Tipps
Das einfachste Python-Memo in Japan (Klassen und Objekte)
[Python] Pip und Wheel
Python Iterator und Generator
Python-Pakete und -Module
Vue-Cli- und Python-Integration
Python-Klassen sind langsam
Ruby, Python und Map
Python-Eingabe und Ausgabe
Python und Ruby teilen sich
Python-Grundkurs (13 Klassen)
Verstehen Sie die Eingabe von Python-Enten
Python asyncio und ContextVar
Listet Methodenargumentinformationen für Klassen und Module in Python auf
[Einführung in Data Scientists] Grundlagen von Python ♬ Funktionen und Klassen
Programmieren mit Python und Tkinter
Ver- und Entschlüsselung mit Python
Python: Klassen- und Instanzvariablen
3-3, Python-Zeichenfolge und Zeichencode
Python 2-Serie und 3-Serie (Anaconda Edition)
Python und Hardware-Verwenden von RS232C mit Python-
Python auf Ruby und wütend Ruby auf Python
Python-Einzug und String-Format
Python Real Number Division (/) und Integer Division (//)
Installieren Sie Python und Flask (Windows 10)
Informationen zu Python-Variablen und -Objekten
Apache mod_auth_tkt und Python AuthTkt
Å (Ongustorome) und NFC @ Python
Lernen Sie Python-Pakete und -Module kennen
# 2 [python3] Trennung und Kommentar aus
Flache Python-Kopie und tiefe Kopie
Python und Ruby Slice Memo
Python-Installation und grundlegende Grammatik
Ich habe Java und Python verglichen!
Flache Python-Kopie und tiefe Kopie
Python-Kurs zum Lernen mit Chemoinfomatik
Über Python, len () und randint ()
Informationen zu Python-Datums- und Zeitzone
Installieren Sie Python 3.7 und Django 3.0 (CentOS)
Paiza Python Primer 8: Grundlegendes zu Klassen
Python-Umgebungskonstruktion und TensorFlow
Python-Klassen- und Instanzvariablen
Ruby- und Python-Syntax ~ branch ~
[Python] Python und Sicherheit - is Was ist Python?