Python 鸭子类型和抽象基类

2020年10月16日 52点热度 0人点赞 0条评论

鸭子类型就是说在调用实例方法的时候,不会检查类型。

Ex:

# 列表扩展
a = [1,2,3]
b = [4,5,6]
a.extend(b)
print(a)
>>> # [1,2,3,4,5,6]

# 不检查类型,也能成功扩展
a = [1,2,3]
b = (4,5,6) # 元组
# c = "456" # 字符串
a.extend(b)
print(a)
>>> # [1,2,3,4,5,6]

参数要求:可迭代对象(iterable),都能进行扩展

file

多态

定义时的类型和运行时的类型不一样,就称为多态。

class Duck(object):
    def info(self):
        print('我是人')

class Dog(object):
    def info(self):
        print('我是狗')

class Cat(object):
    def info(self):
        print('我是猫')

# 多种形态去实现,就是多态
animal_list = [Duck, Dog, Cat]
for animal in animal_list:
    animal().info() # animal() 需要实例化,才能调用info()

抽象基类(abc模块)

抽象基类(abstract base class, ABC),类里定义了纯虚成员函数的类。纯虚函数中提供了接口,并没有具体实现。抽象基类不能被实例化,也就是不能创建对象,通常是作为基类供子类继承,子类中重写虚函数,实现具体接口。

所谓的抽象类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。

比如,动物类有不同的行为,行为不一样,就使用了抽象基类去书写行为,但是不去实现它。用子类去继承动物类,再次实现行为的方法。

抽象基类应用场景
  • A)我们去查找某个类中是否有某种方法
  • B)我们需要强调某个子类必须实现某些方法
A)检查某个类中是否有某种方法
  • 定义Demo类,类中含有__len__魔法方法
  • 导入抽象基类中的Sized类
class Demo(object):
    def __init__(self, li):
        self.li = li

    def __len__(self):
        return len(self.li)

l = ["kevin", "John", "YY"]
d = Demo(l)
print(d)  # <__main__.Demo object at 0x000001DA6D91D828>
print(len(d))  # 会触发__len__ return len(self.li) 返回3

判断类里是否含有有这个__len__魔法方法,要怎样去判断?

print(hasattr(d, "__len__"))  # d = 传入对象,__len__ 传入方法
>>> True  # 判断后,返回的值是布尔值

file

以上定义的Demo 类和抽象基类又有什么关系呢?

# 导入抽象基类的Sized子类
from collections.abc import Sized  # collection 封装了很多数据类型的模块
# 判断d 是否是 Sized 子类
print(isinstance(d, Sized))  # d = 对象, Sized = 类
>>> True  # True 说明d 是 Sized 子类

file

可以判断类是否是一个抽象基类的一个子类,是否含有类型/方法的判断

注意:

  • 抽象基类虽然可以成为别的类的父类,但是别的类并不会继承抽象基类的方法和属性
B)强制子类必须实现父类的方法

例子:

  • 定义父类Cache
    • 封装CRUD方法(增删改查)。强制子类重写该方法
  • 定义子类Redis
class CacheBase(object):
    def delete(self):
        raise NotImplementedError  # Raise 主动抛出异常 NotIm..

    def crea(self):
        raise NotImplementedError

class RedisBase(CacheBase):
    """
    1.子类 如果不重写 父类 方法 访问时 直接抛出异常
    """
    # 强制子类重写父类的方法,不会报错
    def crea(self):
        print("create")

r = RedisBase()
r.crea()
# r.delete()

使用abc模块中的装饰器:

import abc

# 不是直接继承object 而是继承abc.ABCMeta
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def dele(self):
        pass

    def crea(self):
        pass

class RedisBase(CacheBase):
    def dele(self):
        pass

r = RedisBase()  # 在实例化的时候就已经检测到没有重写,不需要调用

抽象基类:实例化的时候检测

主动抛出异常:调用时才会检测

YenYoong☕

当你能梦的时候就不要放弃梦~😎

文章评论