鸭子类型和多态
鸭子类型与多态在前面基础学习中也说过多态,多态就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果的。多态的条件是 继承与重写。所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
鸭子类型 : 动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚"鸭子类型"。
# 鸭子类型举例1
a= [1, 2]
b= {3, 4} # 集合
c= (5, 6) # 元组
a.extend(b)
a.extend(c)
print(a)
extend 方法源码的参数为 iterable(可迭代对象),所以可迭代的元组,集合等即可以使用for循环的等都可以被调用
# 鸭子类型举例2
class Cat(object):
def say(self):
print('I am cat')
class Dog(object):
def say(self):
print('I am dog')
class Duck(object):
def say(self):
print('I am duck')
animal_list = [Cat, Dog, Duck]
for animal in animal_list:
animal().say()
运行结果
I am cat
I am dog
I am duck
在这个例子中,Cat,Dog,Duck 均有say方法
抽象基类(abc模块)
抽象基类(abstract base class,ABC):抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现。抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。
抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
应用场景
1 判断某个对象的类型
2 我们需要强制某个子类必须实现某些方法
class Demo(object):
def __init__(self, names):
self.names = names
def __len__(self):
return len(self.names)
def __iter__(self):
pass
def test(self):
pass
d = Demo(['python', 'juran'])
# print(len(d)) # 2
print(hasattr(d, 'test')) # 判断d中有没有test 方法
print(isinstance(1, int))
from collections.abc import Sized, Iterable
print(isinstance(d, Sized)) # 判断d的中有没有__len__ 方法,如果有就说明d数语Sized类型
print(isinstance(d, Iterable)) # 判断d中有无__iter__ 方法
在源码中Sized方法判断d的中有没有__len__ 方法, Iterable方法判断d中有无__iter__ 方法
# 我们需要强制某个子类必须实现某些方法
import abc
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get(self, name): # 定义get方法
pass
@abc.abstractmethod
def set(self, name, age): # 定义set方法
pass
class RedisBase(CacheBase): # 继承父类CacheBase
def get(self, name): # 重写get 方法
pass
def set(self, name, age): # 重写set方法
pass
ac = RedisBase()
ac.get('douben')
在这里说明,如果没有在子类中重写父类方法,实例化就会报错
使用isinstance和type的区别
首先来说明一下is和== 的区别,is 判断的是内存地址,==判断的是值(value)
isinstance(obj1, obj2)判断obj1是否为obj2的类型
a = 1
b = 'douben'
print(type(a)) # int
print(isinstance(a,int)) # True
print(type(b) is str) # True
print(type(b) == str) # True
print(isinstance(b,str)) # True
在类中对isinstance和type进行比较
class A:
pass
class B(A):
pass
b = B()
print(isinstance(b,B)) # True
print(isinstance(b,A)) # True
print(type(b) is B) # True
print(type(b) is A) # False
所以 isinstance考虑类的继承关系,type没有考虑类的继承关系
类变量和对象变量
在基础学习内有具体介绍,在这里总结几个注意事项
1 类属性只能通过类对象来修改,无法通过实例对象来修改
2 改变类变量的值会作用于该类所有的实例化对象
3 实例属性只能通过实例属性来访问和修改,类对象无法访问和修改
类属性和实例属性查找顺序
MRO算法
图一
图二
缺点
在深度优先中,在图二中,比如顺序是A,B,D, C,可是如果D中有些方法和C中重名或者C中对D中的有些方法重写,就不会被调用到,
在广度优先中图一的顺序为A,B,C,D,E,C和D的也可能出现上面的问题
所以在Python2.3之后,Python采用了C3算法
Python新式类继承的C3算法:https://www.cnblogs.com/blackmatrix/p/5644023.html
对图一
class D(object):
pass
class B(D):
pass
class E(object):
pass
class C(E):
pass
class A(B, C):
pass
print(A.__mro__)
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
对图二
class D(object):
pass
class B(D):
pass
class C(D):
pass
class A(B, C):
pass
print(A.__mro__)
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
Python对象的自省机制
自省是通过一定的机制查询到对象的内部结构
Python中比较常见的自省(introspection)机制(函数用法)有: dir(),type(), hasattr(), isinstance(),通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。
class classmates(object):
name = 'douben'
class Boys(classmates):
def __init__(self, boy):
self.boy = boy
people = Boys('juran')
print(people.__dict__) # {'boy': 'juran'}
print(dir(people)) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref __', 'boy', 'name']
一般来说 dir() 比__dirt__更强大
特例
a = [2, 3, 4]
print(a.__dict__) # AttributeError: 'list' object has no attribute '__dict__'
print(list.__dict__) # 存在
super函数
在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现
class A(object):
def __init__(self):
print("A")
class B(A):
def __init__(self):
super().__init__()
print('B')
b = B() # A B
class Students(object):
def __init__(self, name):
self.name = name
def tell(self):
print("我的名字是 %s" % self.name)
class Dault(Students):
def __init__(self, name, age):
super().__init__(name) # 或者 Students.__init__(self,name) 相当于 self.name = name
self.age = age
def speak(self):
print('我的名字是%s今年%d岁了' % (self.name, self.age))
d = Dault('juran', 13)
d.speak()
d.tell()
# 我的名字是juran今年13岁了
# 我的名字是 juran
实际上super函数就相当于父类中的实例化属性
super 执行顺序到底是什么样的?
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class E:
pass
class D(B, C):
def __init__(self):
print("D")
super().__init__()
d = D()
print(D.__mro__) # D B C A
super 调用父类中的方法是按照 mro 算法来调用的