python中的抽象类、接口类、元类、动态类、新式类、旧式类

元类有什么用

元类可以动态地创建类,它是制造类的工厂

类(class)实例化 一个 instance对象

type 的实例化就是 类class对象

type 是 Python 自建的元类

type () 函数能返回一个对象的类型 (类型本身也是对象),也就是返回对象的__class__属性值。

如何创建 class 对象呢?

最简单的办法

ini 复制代码
class MyClass: #python3默认是创建新式类,隐式继承了object
    a = 1

type函数

go 复制代码
type(name, bases, namespace)

MyClass = type('MyClass', (object,), dict(a=1))

-   name 对应于类的`__name__`属性
-   bases 对应于类的`__bases__`属性
-   namespace 对应于类的`__dict__`属性

动态类

type() 函数可以动态创建类,即在运行期动态创建类

如何控制类的创建行为?

使用 metaclass

比如我们想让自定义的类所创建的的对象自动包含 add 方法,即使类本身没有定义这个方法

python 复制代码
# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
    # __new__ 是在__init__之前被调用的特殊方法
    # __new__是用来创建对象并返回这个对象
    # 而__init__只是将传入的参数初始化给对象
    # 实际中,你很少会用到__new__,除非你希望能够控制对象的创建
    # 在这里,类是我们要创建的对象,我们希望能够自定义它,所以我们改写了__new__
    # 如果你希望的话,你也可以在__init__中做些事情
    # 还有一些高级的用法会涉及到改写__call__,但这里我们就先不这样.
    def __new__(cls, name, bases, attrs):
        # cls:当前创建的类对象
        # name:类的名字
        # bases:类继承的父类集合
        # attrs:类的方法集合。
        print('name: ', name) # 这些打印会在创建MyList类对象的时候打印,而非在创建MyList实例对象的时候
        print('bases: ',bases)
        print('attrs: ', attrs)
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class MyList(list, metaclass=ListMetaclass):
    pass

输出

python 复制代码
name:  MyList
bases:  (<class 'list'>,)
attrs:  {'__module__': '__main__', '__qualname__': 'MyList'}
>>> l=MyList() # 在创建MyList类的时候自动获得了add属性,即一个函数对象
>>> l.add('test1')
>>> l.add('test2')
>>> l
['test1', 'test2']

接口类

一种规范 规定子类应该具备的功能

定义了一些接口(就是函数,但这些函数都没有具体的实现),必须由引用类继承接口类

  • 接口的设计目的是 "协作" 与 "解耦合"
  • 接口类中没有实现所有的方法
  • 鸭子类型算是一种接口类的实现风格
ruby 复制代码
class Payment: # 接口类
    def pay(self, money):
        raise NotImplementedError

class AliPay(Payment): # 引用类具体实现
    def pay(self, money):
        print('AliPay')

class AppPay(Payment):
    def pay(self, money):
        print('AppPay')

class WeichatPay(Payment):
    def zhifu(self, money):
        print('WeichatPay')

def pay(payment, money):
    # 如果实现类没有提供pay方法,在运行的过程就会报异常,
    # 这也是我们接下来要介绍的抽象类存在的目的
    payment.pay(money)
    
>>> p = WeichatPay()
>>> pay(p, 40) # 抛出NotImplementedError

抽象类

一种规范 规定子类应该具备的功能

该类不能被实例化,只能被继承,且子类必须实现抽象方法

  • 抽象类的设计目的更多的是 "复用"
  • 抽象类实现部分方法
  • abc 模块就是用来实现抽象类的
  • 抽象基类的目的就是让别的类继承它以及实现特定的抽象方法
  • 可以去使用抽象基类做类型检查,这样就确保了子类实现了某些特定的方法

实现抽象基类的方法就是通过使用 abc 这个内建模块

python 复制代码
import abc

class Payment(abc.ABC):    # 使用抽象基类实现接口类
    # Payment是一个抽象基类,因为它继承了abc.ABC。
    # 另外,你也可以让它从元类ABCMeta直接创建

    # abc.abstractmethod这个装饰器去标记抽象方法
    # 实现细节由子类去完成
    @abc.abstractmethod
    def pay(self):
        pass

class AliPay(Payment): # 引用类具体实现
    def pay(self, money):
        print('AliPay')

class AppPay(Payment):
    def pay(self, money):
        print('AppPay')

class WeichatPay(Payment):
    def zhifu(self, money):
        print('WeichatPay')
        
>>> p = AliPay()
>>> isinstance(p, Payment)
True
>>> wp = WeichatPay() # 这里就直接报错了
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Cant instantiate abstract class WeichatPay with abstract methods

新式类和旧式类

Python 2.x 中默认都是经典类,只有显式继承了 object 才是新式类。

Python 3.x 中默认都是新式类,不必显式的继承 object

新式类对象可以直接通过__class__属性获取自身类型

新式类增加了__slots__内置属性,可以把实例属性的种类锁定到__slots__规定的范围之中

新式类采用广度优先搜索(bfs),而旧式类是采用深度优先搜索(dfs)

新式类增加了__getattribute__方法

继承搜索的顺序发生了改变

经典类多继承属性搜索顺序:

先深入继承树左侧,再返回,开始找右侧; 深度优先搜索(dfs)

新式类多继承属性搜索顺序

先水平搜索,然后再向上移动 广度优先搜索(bfs)

相关推荐
专注VB编程开发20年2 分钟前
asp.net mvc如何简化控制器逻辑
后端·asp.net·mvc
用户67570498850232 分钟前
告别数据库瓶颈!用这个技巧让你的程序跑得飞快!
后端
千|寻1 小时前
【画江湖】langchain4j - Java1.8下spring boot集成ollama调用本地大模型之问道系列(第一问)
java·spring boot·后端·langchain
程序员岳焱1 小时前
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
后端·sql·mysql
龚思凯1 小时前
Node.js 模块导入语法变革全解析
后端·node.js
天行健的回响1 小时前
枚举在实际开发中的使用小Tips
后端
wuhunyu1 小时前
基于 langchain4j 的简易 RAG
后端
techzhi1 小时前
SeaweedFS S3 Spring Boot Starter
java·spring boot·后端
写bug写bug2 小时前
手把手教你使用JConsole
java·后端·程序员