专题11:python之类的详解(上)—面向对象、类的定义和使用、相关概念
专题12:python之类的详解(中)—封装、继承、多态,C3算法的介绍
专题13:python之类的详解(下)—实例方法、静态方法和类方法详解(包含区别和用法)
封装
使用封装能隐藏对象实现细节,使代码更易维护,
同时因为不能直接调用、修改对象内部的私有信息,在一定程度上保证了系统安全性。
类通过将函数和变量封装在内部,实现了比函数更高一级的封装。
class Person(object):
def __init__(self):
self.__name='a'
self.age = None
@property # 使用@property将一个方法name变成属性,可以直接.name访问
def name(self): # 封装self.__name属性
return self.__name
p1=Person()
p1.name
p1.age
继承
单继承
以下的示例代码 主要包含的知识点:继承的定义 构造函数的继承 子类属性的定义 子类对父类方法的重写
class People(): # 定义一个父类
def __init__(self,name,age,gender): # 父类属性
self.name = name
self.age = age
self.gender = gender
def speak(self): # 父类方法
print("%s 说: 我 %d 岁。" % (self.name, self.age))
# 1.经典类的写法: 父类名称.__init__(self,参数1,参数2,...)
# 2.新式类的写法:super(子类,self).__init__(参数1,参数2,....)
class Student(People):
def __init__(self,name,age,gender,no):
super(Student,self).__init__(name,age,gender) # People.__init__(self, name,age,gender) #继承父类的构造方法
self.no = no # 子类的属性
def score(self): # 子类的方法
return "60分,及格了"
def speak(self): # 子类对父类方法的重写
print("我是学生")
class Teacher(People):
pass
C3算法
在介绍多继承之前需要先了解一下python中的继承算法—C3算法
判断mro要先确定一个线性序列,然后查找路径由序列中类的顺序决定。所以C3算法就是生成一个线性序列。
通用公式
mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )(其中Child继承自Base1, Base2)如果继承至一个基类: class B(A) 这时B的mro序列为[B,A]
如果继承至多个基类: class B(A1,A2,A3 …)
这时B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) …, [A1,A2,A3])
merge操作就是C3算法的核心。
遍历执行merge操作的序列,如果一个序列的第一个元素,是其他序列中的第一个元素,或不在其他序列出现,则从所有执行merge操作序列中删除这个元素,合并到当前的mro中。
merge操作后的序列,继续执行merge操作,直到merge操作的序列为空。
如果merge操作的序列无法为空,则说明不合法。
这里简单介绍一下,如果有详细了解的可以看看源码,或者推荐看一下这篇文章C3 线性化算法与 MRO
源码
#-*- encoding:GBK -*-#
def mro_C3(*cls):
if len(cls)==1:
if not cls[0].__bases__:
return cls
else:
return cls+ mro_C3(*cls[0].__bases__)
else:
seqs = [list(mro_C3(C)) for C in cls ] +[list(cls)]
res = []
while True:
non_empty = list(filter(None, seqs))
if not non_empty:
return tuple(res)
for seq in non_empty:
candidate = seq[0]
not_head = [s for s in non_empty if candidate in s[1:]]
if not_head:
candidate = None
else:
break
if not candidate:
raise TypeError("inconsistent hierarchy, no C3 MRO is possible")
res.append(candidate)
for seq in non_empty:
if seq[0] == candidate:
del seq[0]
多继承
多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。
事例1
class D:
pass
class C(D):
pass
class B(C):
def show(self):
print("i am B")
pass
class G:
pass
class F(G):
pass
class E(F):
def show(self):
print("i am E")
pass
class A(B, E):
pass
a = A()
a.show()
上面的这段代码输出的结果是?i am B
其实呢,就是下面的这种继承关系
如果B类中没有show() 而是在 D类呢?执行结果是 “i am D”,
左边具有深度优先权,搜索的顺序如下
事例2
还有一种继承情况就是,假设D类 和G类 都继承了 H类 当只有B类 和E类有 show方法的时候,无疑和上面的例子一样,找到B就不找了,直接打印"i am B"。但如果是只有H和E有 show方法呢?
class H:
def show(self):
print("i am H")
pass
class D(H):
pass
class C(D):
pass
class B(C):
pass
class G(H):
pass
class F(G):
pass
class E(F):
def show(self):
print("i am E")
pass
class A(B, E):
pass
a = A()
a.show()
你可能会以为会打印 “i am H”,但是,打印的却是 “i am E”!为什么?因为在这种情况下,Python的搜索路径是这样的:
到这里呢,python中的继承就讲的差不多了,归根结底就这些内容,不是特写的复杂,重要的是理解继承的原理,学会使用一些方法。多么复杂的多继承,基本是上边划分的两种情况,其实C3算法不需要会,想学习的可以了解一下
最后通过一张图片分析一下C3算法
多态
直接先看一段代码,帮助你理解多态
class Animal():
def eat(self):
return "吃食物"
class Dog(Animal):
def eat(self):
return "我吃骨头"
class Cat(Animal):
def eat(self):
return "我吃鱼"
class Pig(Animal):
def eat(self):
return "我吃蔬菜"
# 这个函数接收一个animal参数,并调用它的kind方法
def eat_food(animal):
animal.eat()
d = Dog()
c = Cat()
p = Pig()
eat_food(d)
eat_food(c)
eat_food(p)
狗、猫、猪都继承了动物类,并各自重写了 eat方法。eat_food函数接收实例化对象为参数,调用它的 eat 方法。可以看出,无论我们给animal传递的是狗、猫还是猪,调用相应的方法,打印对应的信息。这就是多态。
由于python是一门动态类型的语言,在调用实例方法不检查类型,只要方法存在参数正确,就可以直接调用。这就是python中的多态,没有严格意义上的继承体系,我们称之为 鸭子类型——一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
最后还想说一下这个super,文末突然想起来了
super的用途呢,主要两种情况
- 1.对父类构造函数的重写
- 2.在子类中调用父类的方法