一.课程安排
深入解析类和对象
二.课程内容
1.鸭子类型和多态
- 多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚"鸭子类型"
- Python是一门动态语言
- 动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
a = [1, 2]
b = [3, 4]
c = (5, 6) # touple
d = {7, 8} # set
dict = {'a': 10, 'b': 12} # dict
a.extend(b)
print(a) >>>[1, 2, 3, 4]
a.extend(c)
print(a) >>> [1, 2, 3, 4, 5, 6]
a.extend(d)
print(a) >>> [1, 2, 3, 4, 5, 6,8, 7]
a.extend(dict.values())
print(a) >>> [1, 2, 3, 4, 5, 6,8, 7, 10, 12]
# 总结:
# def extend(self, iterable): # iterable 可迭代的对象 可以用for循环
- 所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
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
- python只有在运行的时候才知道类型,这就是多态。
2.抽象基类(abc模块)
- 抽象基类(abstract base class,ABC):抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现。抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。
- 抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
- 应用场景
- 判断某个对象的类型
- 我们需要强制某个子类必须实现某些方法
'''
判断某个对象的类型
'''
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(['juran', 'python'])
from collections.abc import Sized,Iterable
print(isinstance('demo', int))
>>> False
print(isinstance(d, Iterable))
>>> True
# 使用isinstance判断是否是实例,导入collections.abc 的Iterable,其实是判断在实例d中是否包含__iter__方法,如果有,返回True,否则返回False
- 使用isinstance和type的区别
# == 和 is 的区别:
# ==判断的是 value; is判断的是 内存地址
# isinstance 和 type的区别:
i = 1
s = 'a'
isinstance(i,int)
>>> true
isinstance(s,str)
>>> true
if isinstance(i, int):
print(123)
>>> true
if type(i) is int:
print(123)
>>> true
# ==判断的是 value; is判断的是 内存地址
class A:
pass
class B(A):
pass
b = B()
print(isinstance(b, B))
>>> True
print(isinstance(b, A))
>>>True # True 考虑类的继承关系
print(type(b) is B)
>>> true
print(type(b) is A)
>>>False # False 没有考虑类的继承关系
- 类变量和对象变量
class A:
# 类属性
aa = 1
# 实例方法
def __init__(self, x, y):
# 实例属性
self.x = x
self.y = y
a = A(1, 2)
A.aa = 11 # 类属性赋值
a.aa = 22 # 相当于在__init__函数中增加了一个:self.aa=22
print(a.aa)
>>>22
print(A.aa)
>>>11
b = A(1, 2)
print(b.aa)
>>> 11
- 类属性和实例属性以及查找顺序
MRO算法
Python2.2之前的算法:金典类
DFS(deep first search):A->B->D->C->E
Python2.2版本之后,引入了BFS(广度优先搜索)
BFS:A->B->C->D
在Python2.3之后,Python采用了C3算法
Python新式类继承的C3算法:https://www.cnblogs.com/blackmatrix/p/5644023.html
- 针对上面2张图的查找顺序
# 图1的运行顺序
class D:
pass
class B(D):
pass
class E:
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'>)
# 图2的运行顺序
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(),通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。 -
dict()
class Person(object):
name = 'juran'
class Student(Person):
def __init__(self, school_name):
self.school_name = school_name
user = Student('逻辑教育')
print(user.__dict__)
>>> {'school_name': '逻辑教育'} # 没有显示父类的name属性
- dir()
class Person(object):
name = 'juran'
class Student(Person):
def __init__(self, school_name):
self.school_name = school_name
user = Student('逻辑教育')
print(dir(user))
>>>['__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__', 'name', 'school_name']
- super函数
在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现
class People(object):
def __init__(self, name, age, weight):
self.name = name
self.age = age
self.weight = weight
def speak(self):
print("%s 说:我%d岁了"%(self.name, self.age))
class Student(People):
def __init__(self,name, age, weight, grade):
self.name = name
self.age = age
self.weight = weight
self.grade = grade
# 还可以这么写1:
def __init__(self,name, age, weight, grade):
super().__init__(name ,age, weight)
self.grade = grade
# 还可以这么写2:
def __init__(self,name, age, weight, grade):
People.__init__(self, name, age, weight)
self.grade = grade
def speak(self):
print("%s 说:我%d岁了 我在读%d年级"%(self.name, self.age, self.grade))
- 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 算法来调用的