Python class and object study notes-abstract base class

4.2_Abstract base class (abc module)

Reference blog

1. Preface (nonsense, don’t read it)

An abstract base class is like javaan interface in javaChina , where it cannot implement multiple inheritance, that is, it can only inherit one class, but it can inherit multiple interfaces, and interfaces cannot be used for instantiation.

The pythonabstract base class cannot be instantiated either. pythonIt is a dynamic language, and there is no need to declare variable types when defining variables. A variable is just a symbol and can point to any type of object.

We can copy any type of data to pythonany variable in and can modify it. There is no need to javaimplement a polymorphism like that, because pythonit is a language that supports polymorphism.

Compared with static languages, dynamic languages ​​do not need to specify variable types. This will cause if you pythonwrite wrong code in it, you will find the error only when you run it, that is, you cannot do type checking.

When we use the design python class, we must put the duck type in the first place. What characteristics or types a class has depends on what magic functions it implements inside. Different magic functions give the class different characteristics.

Duck types and magic functions form the basis of the entire python language, which can also be said to be a protocol in python. It can be said that python itself does not implement certain features by inheriting a certain class or a certain interface, but by specifying the class of magic functions to make it into a certain type of object. For example, the string __repr__representation: , __str__; iteration Related: __iter__, __next__; you can call: __call__and so on. Follow this protocol when programming.

Two, abstract base class

  • Set some methods in the base class. All classes that inherit this base class must override the methods in this abstract base class.
  • Abstract base classes cannot be used for instantiation.
  • The role of the abc module:
    Python itself does not provide abstract classes and interface mechanisms. If you want to implement abstract classes, you can use the abc module. abc is the abbreviation of Abstract Base Class.

Question

  • Since python is designed based on the duck type, why does it have the concept of abstract base class? Isn't it enough to implement certain methods directly? Why do you have to inherit?

Answer

Two application scenarios of abstract base class

  • The first application is to check whether a certain class has a certain method (determine the type of an object)
class Company(object):
    def __init__(self,employee_list):
        self.employee = employee_list
    def __len__(self):
        return len(self.employee)
   
com=Company(['ming','ming2'])
print(hasattr(com,'__len__')) # True 
# hasattr内置函数 某一个对象是否具有某种属性,类中函数其实就是一个属性
print(len(com)) # 2 

So those who have learned javait should know that we are more inclined to judge what type Companyof comobject the class instantiates . Instead of hasattr(com,'__len__')judging an object by calling this method.

Based on the above content, the first application scenario of the abstract base class is led to determine the type of a certain type of object. If there is no abstract base class, hasattr()this method must be used . The use of abstract base classes becomes the following way:

from collections.abc import Sized
isinstance(com,Sized) # True
# isinstance 判断某一对象是否是指定的类型

That is to say, you can have a __len__method by inheriting Sized , and instance(对象, Sized)return True at this time

Finally, take a look at the source code of the Sized abstract base class imported above

#所有的抽象基类中的metaclass都必须是ABCMeta
class Sized(metaclass = ABCMeta):
    __slots__ = ()
    @abstractmethod
    def __len__(self):
        return 0
    
    @classmethod
    def __subclasshook__(cls,C):
        if cls is Sized:
            return _check_methods(C,'__len__')
        return NotImplemented
  • The second application is to force a certain subclass to implement certain methods

    Such an internal implementation of a webframework (e.g. Django), the frame may be desirable cachecache integrated in, and may be desirable in the future redisor cacheor memorychachereplaced, or may be used redisor cacheor memorycachecustomize a component, to replace in existing cacheshort seamless integrated.

    So we need to design an abstract base class and specify that the subclass must implement certain methods. For example, when writing a system, when writing the framework, we don’t know whether people who use the framework will replace it with redisor cachewait, but we hope that after writing these, users do not need or reduce the need to call redisor cachewait. Code, so an abstract base class (primary key) will be agreed in advance

class CacheBase():
    def get(self,key):
        pass
    def set(self,key,value):
        pass

CacheBaseThe abstract base class defines getmethods (get data from the cache and get it key) and setmethods (add value). When inheriting this abstract base class, users must implement these two methods again (if you don’t implement the call, there will be an error, so Will force users to implement these methods).

If these interfaces are not defined in advance, the user will return to the class to rewrite the code in the later use; if a contract is made in advance, the user only needs to implement a subclass of the abstract class and configure it in the configuration file. The primary key is If implemented as agreed, it is very convenient to replace.

It is often necessary to consider when writing a framework, which is very helpful to the scalability of the system.

3. How to simulate an abstract base class?

code show as below

#假如有一个抽象基类CacheBase,用户在继承它时必须实现get,set方法
class CacheBase():
    def get(self,key):
        raise NotImplementedError
    def set(self,key,value):
        raise NotImplementedError

class RedisCache(CacheBase):
    pass

redise_cache = RedisCache()
redis_cache.set('key','value') # NotImplementedError
# 调用set方法会报错

If the subclass reimplements the setmethod, the method of the subclass will be called without throwing an exception.

class CacheBase():
    def get(self,key):
        raise NotImplementedError
    def set(self,key,value):
        raise NotImplementedError

class RedisCache(CacheBase):
    def set(self,key,value):
        pass

redise_cache = RedisCache()
redis_cache.set('key','value') # 无异常

The disadvantage of the above simulation is that this exception will be thrown when the set method is called. What if we want to throw an exception while initializing the object?

Need to use the abcmodule, it has two, one is under the overall situation abc, the other is collectionsinside abc.

  • abcImplement abstract base classes with modules
import abc
class CacheBase():
    
    @abc.abstractmethod #装饰器 抽象方法(设计方法)
    def get(self,key):
        pass
    
    @abc.abstractmethod #装饰器 抽象方法(设计方法)
    def set(self,key,value):
        pass
        
class RedisCache(CacheBase): #没有重写方法
        pass

redise_cache = RedisCache()
# redis_cache.set('key','value') # 无异常

The above code RedisCachethrows an exception during initialization because the subclass does not overload the methodTypeError: Can't instantiate abstract class RedisCache with abstract methods get,set

class RedisCache(CacheBase): 
    def get(self,key):
        pass
    def set(self,key,value):
        pass

redise_cache = RedisCache() #RedisCache子类实现get和set方法后,运行不抛异常了
  • Some general abstract base classes have been implemented in python, so that we can understand the interface of the data structure. _collections_abc.pyYou can see many defined abstract base classes at the beginning of the source file.
from abc import ABCMeta, abstractmethod
import sys

__all__ = ["Awaitable", "Coroutine",
           "AsyncIterable", "AsyncIterator", "AsyncGenerator",
           "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
           "Sized", "Container", "Callable", "Collection",
           "Set", "MutableSet",
           "Mapping", "MutableMapping",
           "MappingView", "KeysView", "ItemsView", "ValuesView",
           "Sequence", "MutableSequence",
           "ByteString",
           ]

Just look at one, like the first oneAwaitable

class Awaitable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __await__(self):
        yield

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Awaitable:
            return _check_methods(C, "__await__")
        return NotImplemented

Among them __subclasshook__this magic function is very important.

Recall a question: For the example given at the beginning comis Companyan object instantiated by a Companyclass , and the class does not inherit the abstract base class Sized, then why isinstance(com,Sized)can it be determined that it comis a Sizedtype?

The answer is Sizedthat __subclasshook__this magic function is defined in the abstract base class. The source code is as follows (the complete is given above, and the part is briefly given here)

@classmethod
def __subclasshook__(cls,C):
    if cls is Sized:
        return _check_methods(C,'__len__')
    return NotImplemented

__subclasshook__The function first calls _check_methodsthis function to see if the passed object C has a __len__method, and if so, it returns True. SizedThis magic function in the base class is also being abstracted so that it isinstance(com,Sized)can return True.

Of course, isinstanceas pythona built-in function, the function is definitely more than that simple. Not only call Sizedthe __subclasshook__functions in the class, but also do many other attempts. For example, to find an inheritance chain:

class A:
    pass
class B(A):
    pass
b = B()
print(isinstance(b,A))  # True

Summary: Try to use the pythonduck type as much as possible when using it . It is very flexible. If you must inherit certain interfaces, it is recommended to use Mixin(Python multiple inheritance) to achieve it. It is not recommended to use abstract base classes (then why I learn this ==), Because abstract base classes are easy to overuse and difficult to understand in design.

Guess you like

Origin blog.csdn.net/weixin_43901214/article/details/106864388