1.1 鸭子类型
*多态的概念是应用于java和C#这一类强类型语言中,而python崇尚 “鸭子类型”
动态语言调用实例方法时不检查类型,只要方法在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看作是鸭子。
调用不同的子类将会产生不同的行为,而无须明确知道这个子类实际上是什么,这是多态的重要应用场景。而在python中,因为鸭子类型(duck typing)使得其多态不是那么酷。
鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。*
class Duck():
2 def walk(self):
3 print('I walk like a duck')
4 def swim(self):
5 print('i swim like a duck')
6
7 class Person():
8 def walk(self):
9 print('this one walk like a duck')
10 def swim(self):
11 print('this man swim like a duck')
可以很明显的看出,Person类拥有跟Duck类一样的方法,当有一个函数调用Duck类,并利用到了两个方法walk()和swim()。我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,只要他拥有walk()和swim()方法,就可以正确的被调用。
再举例,如果一个对象实现了__getitem__方法,那python的解释器就会把它当做一个collection,就可以在这个对象上使用切片,获取子项等方法;如果一个对象实现了__iter__和next方法,python就会认为它是一个iterator,就可以在这个对象上通过循环来获取各个子项。
在我理解其本质就是,你可以在你新定义的类中调用别的类中的方法为你这个类所要完成的功能服务,而解释器在你调用这个功能时会把它当在作原来的类的对象调用,就相当于你再调用int len()等方法时可以随意调用,只要你所用的地方与它的属性相对,就是你不可能把一个字符串赋值给一个int类型一样去调用,其本质就是一个调用问题,或在你的代码里重写一个和它一样的功能问题,只不过不用像在Java中进行接口来调用,而是直接调用或再写
1.2 多态
定义的类型和运行时的类型不一样,就称为多态
2.抽象基类
2.1抽象基类介绍
抽象基类(abstract base class,ABC):抽象基类就是**类里定义了纯虚成员函数的类**。**纯虚函数只提供了接口,并没有具体实现**。抽象基类不能被实力换(不能被创建对象),通常只是作为基类供子类继承,子类中重写虚函数,实现具体的接口。
抽象基类就是**定义各种方法而不实现的类**,任何继承自抽象基类的类必须实现这些方法,否则无法实例化
2.2抽象基类应用场景
- 我们去检查某个类中是否有某种方法
- 我们需要强调某个类必须要实现的某些方法
1.检查某个类是否有某种方法
- 定义一个Demo类,类中含有__len__魔法方法
- 导入抽象基类中的Sized类
from collections.abc import Sized
class Demo(Sized):
def __init__(self,elist):
self.elist=elist
def __len__(self):
return len(self.elist)
d=Demo(['wangxing','xuexin'])
print(d,Sized)#<__main__.Demo object at 0x0000014BB9F9FB70> <class 'collections.abc.Sized'>
print(isinstance(d,Sized))#True d是Sized的子类
print(hasattr(d,"__len__"))#True d中含有__len__方法
print(dir(d))#['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', 'elist']输出子类及继承的父类中的所有属性名
- 查看Sized 源代码
python安装路径下lib中的隐藏文件_collection_abc.py
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
注意:注册的虚拟子类不会继承抽象基类的任何方法和属性
2. 强制子类必须实现父类的方法
例子:
- 定义父类Cache
- 封装CRUD方法。强制子类重写该方法。
- 定义子类Redis
第一种,这种方法实例化的时候就会报错,如果继承虚类没有对虚类方法重写
import abc
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def dele(self,key):
pass
@abc.abstractmethod
def crea(self, key,value):
pass
class RedisBase(CacheBase):
def dele(self,key):
pass
def crea(self,key,value):
pass
r=RedisBase()
第二种,这种方法在调用虚方法的时候会报错,如果继承虚类没有对该虚类方法重写
class CacheBase():
# @abc.abstractmethod
def dele(self,key):
raise NotImplementedError
# @abc.abstractmethod
def crea(self, key,value):
raise NotImplementedError
class RedisBase(CacheBase):
def dele(self,key):
pass
# def crea(self,key,value):
# pass
r=RedisBase()
r.dele(2)
r.crea(2,4)#raise NotImplementedError NotImplementedError
3.type与isistance区别
- type不考虑继承关系
- isinstance考虑继承关系
class CacheBase():
# @abc.abstractmethod
def dele(self,key):
raise NotImplementedError
# @abc.abstractmethod
def crea(self, key,value):
raise NotImplementedError
class RedisBase(CacheBase):
def dele(self,key):
pass
# def crea(self,key,value):
# pass
r=RedisBase()
# r.dele(2)
# r.crea(2,4)#raise NotImplementedError NotImplementedError
print(isinstance(r,CacheBase))#True
print(type(r) is CacheBase)#False
print(type(r) is RedisBase)#True
print(type(r))#<class '__main__.RedisBase'>
print(type(RedisBase) is CacheBase)#False
4.类属性与实例属性
4.1基本查找顺序
- 对象是可以向上查找的,所以可以访问到类属性
- 当对象有该实例属性时,则输出自己的
- 类不能向下查找,所以只能访问到类属性
4.2多继承查询顺序
继承关系如下,则属性查找顺序为?