I want to explain the abstract class (ABCmeta) of Python in detail.

0. Introduction

Because the abstract class of Python was unexpectedly special I want to explain a little carefully based on the explanation of the metaclass

1. Basic usage of ABCmeta

Python's Abstract is a bit special, something called a metaclass Define a class by specifying ABCmeta (metaclass will be explained later)

from abc import ABC, ABCMeta, abstractmethod

class Person(metaclass = ABCMeta):
    pass

By the way, because there is also an ABC class, this does not specify the meta class Basically this is easier to understand because it only inherits

class Person(ABC):
    pass

Add @abstractmethod to the method you want to abstract

@abstractmethod
def greeting(self):
    pass

Implement in inherited class

class Yamada(Person):

    def greeting(self):
        print("Hello, this is Yamada.")

Of course, if you do not implement it and instantiate it, an error will occur

class Yamada(Person):
    pass

yamada = Yamada()
---------------------------------------------------------------------------------------------------------------
TypeError: Can't instantiate abstract class Yamada with abstract methods greeting

That's how to use the basic abstract class

1.1 What is a metaclass in the first place?

A little off here, talk about metaclasses, skip if you're not interested

What is a metaclass?

In object-oriented programming, a metaclass is a class whose instance is a class. A metaclass defines a class that is an instance of it, and further defines the behavior of an instance of that class, just as a regular class defines the behavior of that instance.

[Source]: Metaclass-wikipedia </ cite>

I'm not sure, so if you give an example In fact, Python's hidden function (?) Allows you to dynamically define a class from type.

def test_init(self):
    pass

Test_Class = type('Test_Class', (object, ), dict(__init__ = test_init, ))

Of course, you can instantiate this class

Test_Class_instance = Test_Class()
print(type(Test_Class_instance))
-------------------------------------------------------------------------------------------------------------------
<class '__main__.Test_Class'>

Test_Class generated by type at this time can be said to be an instance and a class definition. This is a metaclass in a narrow sense

Surprisingly, the classes that are usually casually defined in Python It can be an instance of type type

class define_class:
    pass

print(type(define_class))
-------------------------------------------------------------------------------------------------------------------
<class 'type'>

After all, Python defines a class internally type ('classname', ...) </ code> is called automatically. It turns out that

Change the class that can be called automatically A super powerful function is the identity of \ _ \ _ metaclass \ _ \ _, ABCmeta is a class that inherits type ABC meta source code

By the way, metaclass is too powerful and changes the language specification itself. It ’s black magic, so you should n’t abuse it.

2. Abstraction of classmethod, staticmethod and property

@abstractclassmethod、@abstractstaticmethod、@abstractproperty Have both been deprecated, so you need to add more decorators

Abstract class

class Person(ABC):

    @staticmethod
    @abstractmethod
    def age_fudging(age):
        pass

    @classmethod
    @abstractmethod
    def weight_fudging(cls, weight):
        pass

    @property
    @abstractmethod
    def age(self):
        pass

    @age.setter
    @abstractmethod
    def age(self, val):
        pass

    @property
    @abstractmethod
    def weight(self):
        pass

    @weight.setter
    @abstractmethod
    def weight(self, val):
        pass

Implementation

class Yamada(Person):

    def __init__(self):
        self.__age = 30
        self.__weight = 120
    
    #10-year-old mackerel reading
    @staticmethod
    def age_fudging(age):
        return age - 10
    
    #20kg mackerel reading
    @classmethod
    def weight_fudging(cls, weight):
        return weight - 20
    
    @property
    def age(self):
        return Yamada.age_fudging(self.__age)
    
    @age.setter
    def age(self):
        return
    
    @property
    def weight(self):
        return self.weight_fudging(self.__weight)
    
    @weight.setter
    def weight(self):
        return

y = Yamada()
print("name:Yamada age:{0}body weight:{1}".format(y.age, y.weight))
-----------------------------------------------------------------------------------------------------------------
name:Yamada age:20 weight:100

It's been a little longer, but the basic usage is the same However, please note that if you do not make @abstractmethod below, you will get an error.

By the way, in Python you can make it private by prepending "__" to the member variable.

3. Multiple inheritance of abstract classes

Multiple inheritance is available in Python, but what about abstract classes?

As you know, multiple inheritance has various problems (diamond inheritance problem, name collision). Basically, it is safer not to use multiple inheritance, but if you use it, it is required to be a Mixin class.

3.1 What is Mixin?

What is a mixin in an object-oriented programming language? It is a class that provides functions by being inherited by subclasses and is not intended to operate independently. Depending on the language There may be a mixin as a separate system from what the language calls a class or inheritance (detailed in the #Variations section).

[Source]: Mixin --wikipedia </ cite>

Also quoted from Wikipedia, but in short A class that doesn't work properly unless inherited </ b> Abstract classes and interfaces are also Mixin classes

So, we will create a beastman class that inherits cats and humans

class Cat(ABC):

    @abstractmethod
    def mew(self):
        pass
    
    #Also in Person
    @abstractmethod
    def sleep(self):
        pass

class Person(ABC):

    @abstractmethod
    def greeting(self):
        pass

    #Also in Cat
    @abstractmethod
    def sleep(self):
        pass

class Therianthrope(Person, Cat):

    def greeting(self):
        print("Hello")

    def mew(self):
        print("Meow")
    
    def sleep(self):
        print("zzz…")

cat_human = Therianthrope()

cat_human.greeting()
cat_human.mew()
cat_human.sleep()
-----------------------------------------------------------------------------------------------------------------
Hello
Meow
zzz…

You can see that it works without difficulty even with ABC meta This time, the name of the method called sleep is collided. There is no problem because it is not implemented in the inheritance source

Finally, as written on the official Python website You should be careful about collisions between metaclasses

4. Multi-stage inheritance of abstract classes

The concern with multi-stage inheritance is whether ABCmeta will inherit when inherited In other words, when Python inherits an abstract class, it becomes an abstract class. The point of becoming

In conclusion, ABCmeta inherits, and if you inherit an abstract class, it becomes an abstract class.

class Animal(metaclass=ABCMeta):
    pass

class Person(Animal):
    pass

class Yamada(Person):
    pass

print(type(Person))
print(type(Yamada))
-------------------------------------------------------------------------------------------------------------------
<class 'abc.ABCMeta'>
<class 'abc.ABCMeta'>

After all, where is the abstract method? It means that it should be implemented

class Animal(metaclass=ABCMeta):

    @abstractmethod
    def run(self):
        pass


class Person(Animal):
    pass

class Yamada(Person):

    def run(self):
        print("12km / h")

y = Yamada()
y.run()
-------------------------------------------------------------------------------------------------------------------
12km / h

5. Take an abstract class as a formal argument

It's been a long time, but at the end it's a rough sketch of the abstract class Introducing an example of usage that can also be used in Python

Actually, even in Python, you can specify the type of formal argument and return value. I specified "Person", which is the parent class and abstract class created earlier, as the formal argument type. Let's create a function called fall_asleep

from abstract import Person
import time

def fall_asleep(p:Person):
    sleep_time = p.sleep()
    time.sleep(sleep_time)
    print("!")
class Person(ABC):

    @abstractmethod
    def greeting(self):
        pass

    @abstractmethod
    def sleep(self) -> float:
        pass

Abstract classes, typing, etc. are no longer Python. This kind of technique is used when creating a slightly larger software in Python. It's useful for something.

For example, the person making fall_asleep can see the actual state of the concrete class (Yamada, Ikeda, Yosida). Don't worry (don't know), no matter how much you change, as long as the function called sleep meets the requirements Since it is completed, it can be said that fall_asleep depends on the abstract class (Person) rather than the concrete class.

This is what the architecture calls the "Dependency Reversal Principle (DIP)". Say

The following items are applicable to DIP

  • Do not refer to (variable) concrete classes
  • Do not inherit (variable) concrete classes
  • Do not override concrete functions

After all, relying on abstract classes is better You can achieve a robust architecture.

y = Yamada()
fall_asleep(y)
-------------------------------------------------------------------------------------------------------------------
zzz…
!

Recommended Posts