zoukankan      html  css  js  c++  java
  • python之抽象类&abc模块+虚拟子类&register

    抽象类和接口:

    java

    我们先从java讲起,没有java基础的可以略过。

    (挖坑) 

    python

    在python并没有抽象类之说,或者说抽象类=接口类(区别于接口)

    继承有两种用途:

    一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)  

    二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

    在python中实现接口类必须借助于 abc模块

    abc模块

    前言:

    面向对象的设计中,抽象类,接口这些必不可少的东西,在python中是如何提现的呢?

    python作为一个动态语言,没有强类型的检查,而是以鸭子类型的方式提现,在执行的时候python不严格要求你必须是继承指定的父类而来,只要在调用的时候你有相应的方法和属性就可以了,长的像鸭子你就是鸭子。

    也正是基于python这样的特性,python中没有interface的概念。

    interface并不是普遍存在的,而是作为一个特例出现在java中,为的是解决多继承的问题。

    python支持多继承,自然没有这个需要了。

    然而我们难免会有这样的需求:子类必须实现某些指定的方法和属性,否则就抛异常。使用一些笨方法自然是可以达到相同的效果,但是python为我们提供了abc模块。

    what is abc:

    import abc
    print([i for i in dir(abc) if not i.startswith("__")])
    ['ABC', 'ABCMeta', 'WeakSet', 'abstractclassmethod', 'abstractmethod', 'abstractproperty', 'abstractstaticmethod', 'get_cache_token']

    How use abc?

    竟然没报错?

    from abc import ABCMeta, abstractmethod
    
    
    class Drawable(metaclass=ABCMeta):
    
        # @abstractmethod
        def size(self):
            return 'Drawable size'
    
        # @abstractmethod
        def draw(self, x, y, scale=1.0):
            pass
    
        def double_draw(self, x, y):
            self.draw(x, y, scale=2.0)
    
    d=Drawable()

    说明。这个接口类竟然可以实例化。

    接下来,我们给这个接口类添加一个abstractmethod方法的时候

    class Drawable(metaclass=ABCMeta):
    
        @abstractmethod
        def size(self):
            return 'Drawable size'
    
        # @abstractmethod
        def draw(self, x, y, scale=1.0):
            pass
    
        def double_draw(self, x, y):
            self.draw(x, y, scale=2.0)
    
    d=Drawable()

    它报错了:TypeError: Can't instantiate abstract class Drawable with abstract methods size

    总结:有抽象方法的接口类才是不能实例化。

    接下来,我们继续看继承关系

    from abc import ABCMeta, abstractmethod
    
    
    class Drawable(metaclass=ABCMeta):
    
        # @abstractmethod
        def size(self):
            return 'Drawable size'
    
        # @abstractmethod
        def draw(self, x, y, scale=1.0):
            pass
    
        def double_draw(self, x, y):
            self.draw(x, y, scale=2.0)
    
    
    class Cicle(Drawable):
        # def size(self):
        #     return 'Cicle size'
    
        # def draw(self, x, y, scale=1.0):
        #     print(x * scale, y * scale)
        def paint(self):
            pass
    
    
    c=Cicle()

    成功了。=》告诉我们继承一个没有抽象方法的接口类,并不用重写也能实例化。毕竟没有抽象方法。

    那有抽象方法的接口类呢?

    1.继承有抽象方法的接口类但没有重写抽象方法=》报错

    from abc import ABCMeta, abstractmethod
    
    
    class Drawable(metaclass=ABCMeta):
    
        @abstractmethod
        def size(self):
            return 'Drawable size'
    
        # @abstractmethod
        def draw(self, x, y, scale=1.0):
            pass
    
        def double_draw(self, x, y):
            self.draw(x, y, scale=2.0)
    
    
    class Cicle(Drawable):=====》继承但没有实现抽象方法
    
        def paint(self):
            pass
    
    c=Cicle()

    TypeError: Can't instantiate abstract class Cicle with abstract methods size

    2,继承有抽象方法的接口类必须重写抽象方法

    from abc import ABCMeta, abstractmethod
    
    
    class Drawable(metaclass=ABCMeta):
    
        @abstractmethod
        def size(self):
            return 'Drawable size'
    
        # @abstractmethod
        def draw(self, x, y, scale=1.0):
            pass
    
        def double_draw(self, x, y):
            self.draw(x, y, scale=2.0)
    
    
    class Cicle(Drawable):
        def size(self):
            return 'Cicle size'
    
        def paint(self):
            pass
    
    c=Cicle()

    成功了

    3.继承有抽象方法的非接口类呢?

    from abc import ABCMeta, abstractmethod
    
    class Drawable:
    
        @abstractmethod
        def size(self):
            return 'Drawable size'
    
        # @abstractmethod
        def draw(self, x, y, scale=1.0):
            pass
    
        def double_draw(self, x, y):
            self.draw(x, y, scale=2.0)
    
    
    class Cicle(Drawable):
    
        def paint(self):
            pass
    
    
    c=Cicle()

    成功====》单独存在抽象方法并没啥用

    进阶:

    1.ABC是python 3.4之后新增的类,之前必须使用metaclass=ABCMeta,现在多了一个简单的方法ABC

    2.abc模块还定义了'abstractclassmethod', 'abstractproperty', 'abstractstaticmethod'三个装饰器,但是因为可以通过@abstractmethod上堆叠实现,就显得多余二废弃了。

    from abc import *
    
    
    class A(ABC):
        
        @property
        @abstractmethod
        def func(self):
            pass
        
        @abstractproperty
        def func2(self):
            pass
        
        @classmethod
        @abstractmethod
        def func3(cls):
            pass
        
        @abstractclassmethod
        def func4(cls):
            pass
        
        @staticmethod
        @abstractmethod
        def func5():
            pass
        
        @abstractstaticmethod
        def func6():
            pass
        

    虚拟子类

    注册虚拟子类的方式在抽象基类上调用register方法

    注册的类就会变成抽象基类的虚拟子类,而且issubclass和isinstance等函数都能识别,但是注册的类并不会从抽象基类中继承任何方法。

    虚拟子类不会继承注册的抽象基类,而且任何时候都不会检查子类是否符合抽象基类的接口(即使在实例化的时候也不会检查)。

    作为抽象类的虚拟子类无需实现其需要的抽象方法
    from abc import *
    class A(metaclass=ABCMeta):
        @abstractmethod
        def func1(self):
            pass
    
    @A.register
    class B:
        def func2(self):
            pass
    
    
    class C(A):
        def func2(self):
            pass
    
    b=B()   #===》作为抽象类的虚拟子类无需实现其需要的抽象方法
    # c=C()==>继承抽象类没有实现抽象方法报错
    print("OK")

    类的继承关系在一个特殊属性中制定--__mro__即方法解析顺序,这个属性会按顺序列出所有 ‘真实的’ 类和其超类。

    from abc import *
    class A(ABC):
        @abstractmethod
        def func1(self):
            pass
        
    class B:
        def func1(self):
            pass
    
    
    @A.register
    class C(B):
        def func2(self):
            pass
        
    
    b=B()
    c=C()
    print(issubclass(C,A))
    print(isinstance(c,A))
    print(isinstance(c,B))
    # True
    # True
    # True
    
    print(C.__mro__)
    # (<class '__main__.C'>, <class '__main__.B'>, <class 'object'>)
    # 没有A类
  • 相关阅读:
    PHP对URL传递的参数值进行编码和解码
    PHP 获取表单【2/2】
    PHP 获取表单【1/2】
    utf8 和 utf-8区别
    PHP 乘法口诀表
    PHP 插入和获取后台数据
    点击复制
    php网盘
    memcached-session-manager配置
    Apache Http Server与Tomcat6 的负载均衡(二)
  • 原文地址:https://www.cnblogs.com/wqbin/p/10239515.html
Copyright © 2011-2022 走看看