一、property:
1.get/set方法:
1.1 隐藏实现细节:在使用对象时,尽量不要让使用者直接操作对象中的属性,这样会带来安全隐患。改进办法,使用私有属性。
1.2 提供精确的访问控制:学习过 set/get方法,是专门来为类的私有属性提供访问接口。
1.3 保证数据有效性:在 set方法中,对传入的数据进行判断有效性,如果是无效数据,提示用户出错。
代码实例:
#!/usr/bin/env python
# coding=utf-8
"""演示通过get/set方法控制类的私有属性"""
class Account(object):
"""定义一个账户类,存在2个私有属性:username和balance"""
def __init__(self, username, money):
self.__username = username # 账户人姓名
self.__balance = money # 账户余额
# 账户人姓名在创建账户的时候就已经确定了,不允许修改但允许查看,所以不提供set方法
def get_username(self):
return self.__username
def get_balance(self):
return self.__balance
# 在定义set方法时,可以加入对属性的控制,只能接收整型数据,balance不能小于0等
def set_balance(self, money):
if isinstance(money, int):
if self.__balance + money >= 0:
self.__balance += money
else:
raise ValueError("账户余额不足...")
else:
raise ValueError("请输入正确的金额...")
if __name__ == '__main__':
xm = Account("小明",100)
print("账户名:", xm.get_username(), ",当前余额:", xm.get_balance() )
xm.set_balance(-50)
print("账户名:", xm.get_username(), ",当前余额:", xm.get_balance())
xm.set_balance(-50)
print("账户名:", xm.get_username(), ",当前余额:", xm.get_balance())
xm.set_balance(-50)
print("账户名:", xm.get_username(), ",当前余额:", xm.get_balance())
运行结果:
账户名: 小明 ,当前余额: 100
账户名: 小明 ,当前余额: 50
账户名: 小明 ,当前余额: 0
Traceback (most recent call last):
File "G:/2_Mysql/day12/代码/get/set.py", line 39, in <module>
xm.set_balance(-50)
File "G:/2_Mysql/day12/代码/get/set.py", line 26, in set_balance
raise ValueError("账户余额不足...")
ValueError: 账户余额不足...
2.property概述:
property本身就是属性、性质,在python中主要为属性提供便利的操作方式。
3.property类:
在python中,提供了一个叫做property的类,通过创建这个类的对象的设置,在使用对象的是由属性时,可以不再使用属性的get/set方法,而像普通的共有属性一样去使用私有属性,为开发提供便利。
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
property是一个类,init方法有四个参数组成,实例化后返回一个property的对象,用来操作属性。fget:属性的获得方法;fset:属性的设置方法;fdel:实行的删除方法;doc:属性的描述
代码演示:
#!/usr/bin/env python
# coding=utf-8
"""演示通过property实例控制类的私有属性"""
class Account(object):
"""定义一个账户类,存在2个私有属性:username和balance"""
def __init__(self, username, money):
self.__username = username # 账户人姓名
self.__balance = money # 账户余额
# 定义私有的get方法,账户人姓名在创建账户的时候就已经确定了,不允许修改但允许查看,所以不提供set方法
def __get_username(self):
return self.__username
def __get_balance(self):
return self.__balance
# 定义私有的set方法,在定义set方法时,可以加入对属性的控制,只能接收整型数据,balance不能小于0等
def __set_balance(self, money):
if isinstance(money, int):
if self.__balance + money >= 0:
self.__balance += money
else:
raise ValueError("账户余额不足...")
else:
raise ValueError("请输入正确的金额...")
# 实例化property类为属性设置便利的访问方式
name = property(fget=__get_username)
balance = property(fget=__get_balance, fset=__set_balance)
if __name__ == '__main__':
xm = Account("小明",100)
print("账户名:", xm.name, ",当前余额:", xm.balance)
xm.balance = -50
print("账户名:", xm.name, ",当前余额:", xm.balance)
xm.balance = -30
print("账户名:", xm.name, ",当前余额:", xm.balance)
xm.balance = -50
print("账户名:", xm.name, ",当前余额:", xm.balance)
运行结果:
账户名: 小明 ,当前余额: 100
账户名: 小明 ,当前余额: 50
账户名: 小明 ,当前余额: 20
Traceback (most recent call last):
File "G:/2_Mysql/day12/代码/02_property对象.py", line 46, in <module>
xm.balance = -50
File "G:/2_Mysql/day12/代码/02_property对象.py", line 29, in __set_balance
raise ValueError("账户余额不足...")
ValueError: 账户余额不足...
分析:
通过 property 类实例对象以后,在使用对象中的属性时,就可以像使用普通公有属性一样来调用,但是实际调用的还是 set/get 方法。 在实例 property 对象时,不是所有的参数都需要写,比如示例中的 name 只提供了 get 方法,并且是一个私有的方法。这样就完全隐藏了内部的实现细节 。
4. @ property装饰器:
4.1 什么时@property装饰器:
Python 语法中,提供一种装饰器语法,在函数定义的上一行,使用 @xxx 的形式来使用装饰器。
装饰器的作用就是提供装饰的功能,在不改变原来函数功能的基础上,添加新的功能。属于语法糖(语法糖见“python高级一:with上下文管理器“)的一种。
4.2 使用@property装饰器完成简化账户类
代码实现:
#!/usr/bin/env python
# coding=utf-8
"""演示通过@property装饰器控制类的私有属性"""
class Account(object):
"""定义一个账户类,存在2个私有属性:username和balance"""
def __init__(self, username, money):
self.__username = username # 账户人姓名
self.__balance = money # 账户余额
# 使用@property装饰器修饰get方法
@ property
def username(self):
return self.__username
@property
def balance(self):
return self.__balance
# 只有先使用@property修饰了get方法,才可以再修饰set方法,且方法名相同.修饰器为@方法名.setter
@balance.setter
def balance(self, money):
if isinstance(money, int):
if self.__balance + money >= 0:
self.__balance += money
else:
raise ValueError("账户余额不足...")
else:
raise ValueError("请输入正确的金额...")
if __name__ == '__main__':
xm = Account("小明",100)
print("账户名:", xm.username, ",当前余额:", xm.balance)
xm.balance = -50
print("账户名:", xm.username, ",当前余额:", xm.balance)
xm.balance = -30
print("账户名:", xm.username, ",当前余额:", xm.balance)
xm.balance = -50
print("账户名:", xm.username, ",当前余额:", xm.balance)
4.3 注意:
- 在使用 @property 装饰属性时,只能装饰获取方法
- @property 装饰属性时, set/get 方法不需要再属性名前加 set 和 get ,直接写属性名即可
- 如果一个属性同时有 set/get 方法,那么要先实现 @property 对获取方法的定义
- 再实现设置方法的定义,定义时使用 @xxx.setter 装饰,xxx 要和获取方法名保持一致
二、魔法属性和魔法方法:
1.魔法属性和魔法方法的概述:
在 Python 中预先定义好了一些以 __xxx__ 形式的属性和方法。这些属性和方法用来表示特定的意义和功能。在程序运行过程中自动调用,或者根据开发的需求手动调用,甚至重写魔法方法的功能。
2.__doc__属性:
2.1 自定义描述:
python中有个特性叫做文档字符串,即 DocString,这个特性可以让你的程序文档更加清晰易懂。DocString通俗的说就是文件中的特殊注释。用来描述文件,类,函数等的功能。
DocString有固定的格式,一般是放在自定义函数的第一行,用"""DocString"""表示。可以使用xxx.__doc__属性,将DocString的文字print出来。
代码实例:
AA.py:
#!/usr/bin/env python
# coding=utf-8
''' This is File DocString '''
def display():
''' This is Display Function DocString. '''
pass
class Test(object):
''' This is Test Class DocString! '''
pass
def show(self):
''' This is Show Function DocString '''
pass
docTest.py:
#!/usr/bin/env python
# coding=utf-8
''' This is File DocString '''
def display():
''' This is Display Function DocString. '''
pass
class Test(object):
''' This is Test Class DocString! '''
pass
def show(self):
''' This is Show Function DocString '''
pass
运行docTest.py得到结果:
This is Test Class DocString!
This is Test Class DocString!
This is Show Function DocString
This is Display Function DocString.
This is File DocString
2.2 系统文件说明:
Python的系统文件都在使用这个特性对功能进行描述。
print(print.__doc__) # 打印print函数的DocString
也可以使用help函数打印某个自定义或者系统类、函数等的DocString
help(print) # 使用help函数打印doc文档
3. __module__属性 、__class__属性 、__bases__属性 、__mro__属性:
3.1 浅谈多继承和多重继承:
在 Python 程序开发过程中经常会导入很多其它模块或者创建很多类的实例对象。并且,Python 是一个支持多继承和多重继承的编程语言。
多继承是指一个类同时继承多个类。 多重继承是指一个类所继承的类之前还有继承关系。
当使用模块较多时,可以通过 __module__ 来查看当前成员属于哪个模块,通过 __class__ 属性查看对象属于哪个类
当类中有复杂的继承关系时,可以通过 __bases__ 查看本类的父类有哪些,通过 __mro__ 属性查看类中方法的查找顺序。
代码实例:
AA.py
#!/usr/bin/env python
# coding=utf-8
"""演示类的多继承和多重继承"""
# 动物类
class Animal(object):
pass
# 人类继承动物类
class Person(Animal):
pass
# 蝙蝠类继承动物类
class Bat(Animal):
pass
# 蝙蝠侠类继承人类和蝙蝠类
class BatMan(Person, Bat):
pass
if __name__ == '__main__':
# 显示蝙蝠侠的福尅
print(BatMan.__bases__)
# 显示蝙蝠侠类初始化或者实例调用方法时的查找顺序
print(BatMan.__mro__)
运行结果:
(<class '__main__.Person'>, <class '__main__.Bat'>)
(<class '__main__.BatMan'>, <class '__main__.Person'>, <class '__main__.Bat'>, <class '__main__.Animal'>, <class 'object'>)
让AA.py作为一个被调用模块,定义一个BB.py调用AA.py
代码:
BB.py
#!/usr/bin/env python
# coding=utf-8
from AA_05 import *
if __name__ == '__main__':
# 使用module查看当前类属于哪个模块
print(BatMan.__module__)
# 使用class查看指定对象属于哪个类
print(BatMan().__class__) # BatMan(),实例化了一个匿名对象
# 使用bases来查看当前类的直接父类
print(BatMan.__bases__)
# 使用mro来查看多重继承时的继承关系
print(BatMan.__mro__)
运行结果:
AA_05
<class 'AA_05.BatMan'>
(<class 'AA_05.Person'>, <class 'AA_05.Bat'>)
(<class 'AA_05.BatMan'>, <class 'AA_05.Person'>, <class 'AA_05.Bat'>, <class 'AA_05.Animal'>, <class 'object'>)
4. __new__方法 和__init__方法:
4.1 python中对象的创建过程:
在 Python 中,__init__ 方法用来对实例中的属性进行初始化操作,在使用类创建实例对象时,就会自动调用这方法。但是 __init__ 方法并不是在创建对象时第一个被执行的方法。
在创建对象时,Pyhton 会先在内存中申请对象的内存空间。如果申请成功,说明对象才创建成功,之后才是使用 __init__ 进行初始化操作。而申请对象空间这个过程就是由 __new__ 方法来执行的。也就是说在创建对象过程中,会先执行 __new__ 在内存中申请实例存储空间,然后再执行 __new__ 初始化实例对象空间。
代码验证:
#!/usr/bin/env python
# coding=utf-8
class A(object):
# 重写new方法
def __new__(cls, *args, **kwargs):
print("__new__")
# 必须有返回值,返回值表示对象创建时申请的空间
return super().__new__(cls, *args, **kwargs)
def __init__(self):
print("__init__")
if __name__ == '__main__':
A()
运行结果:
__new__
__init__
分析:
__new__ 方法在开辟完内存空间后,会自动调用 __init__ 方法来初始化对象空间。开发过程中,一般不会重写 __new__ 方法。一般都是重写 __init__ 方法。
Python官方文档的说法,__new__ 方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。 比如,要继承 int 类,实现一个永远是整数的类。 另外,在实现单例类的时候可以使用 __new__ 。
一般的情况下,不会重写 __new__ 类,只需要理解 __new__ 和 __init__ 的执行顺序即可。
5. __call__方法:
5.1 __call__方法概述:
Python中有一个有趣的语法,只要定义类型的时候,实现__call__函数,这个类型就成为可调用的。 换句话说,我们可以把这个类型的对象当作函数来使用,相当于 重载了括号运算符。我们可以 实例对象()
实现__call__后,可以将对象当做函数一样去使用,称为仿函数或函数对象
5.2 普通函数和函数对象的区别:
普通对象在执行完成后就结束了,不能保存函数执行的状态。而且在扩展其它函数时,函数间的关联性不强。
函数对象可以保存函数的状态。比如要实现对象调用的次数。而且可以在类中扩展更多的功能。
5.3 代码实现:
#!/usr/bin/env python
# coding=utf-8
class TestClass(object):
def __init__(self, name):
self.name = name
def __call__(self, *args, **kwargs):
print("self.name:%s" % self.name)
print("__call___")
def func(self,age):
self.age = age
print(self.name, self.age)
if __name__ == '__main__':
call = TestClass(name="小明")
call()
print("="*20)
call.__call__()
print("="*20)
call.func(18)
运行结果:
self.name:小明
__call___
====================
self.name:小明
__call___
====================
小明 18
分析:当一个类型实现了特殊方法__call__
,该类的实例就变成了可调用的类型, 对象名() 等价于 对象名.__call__()
,有时候可以简化对象的调用,让对象变成可调用的对象, 实现__call__即可。
(重点内容)6. __getitem__ 、__setitem__ 、__delitem__ 、__len__ 方法:
__getitem__(self,key):返回键对应的值。
__setitem__(self,key,value):设置给定键的值
__delitem__(self,key):删除给定键对应的元素。
__len__():返回元素的数量
代码实例:
#!/usr/bin/env python
# coding=utf-8
"""实现学生管理系统"""
class StudentManger(object):
"""学生管理系统"""
def __init__(self):
"""定义一个字典保存学生信息"""
self.__stus = {}
def __setitem__(self, key, value):
"""添加学生信息"""
self.__stus[key] = value
def __getitem__(self, item):
"""获取学生信息"""
if item in self.__stus:
return self.__stus[item]
else:
return None
def __delitem__(self, key):
"""删除学生信息"""
if key in self.__stus:
del self.__stus[key]
def __len__(self):
"""获取学生人数"""
return len(self.__stus)
if __name__ == '__main__':
# 创建学生管理系统对象
sm = StudentManger()
# 添加学生
sm["001"] = {"name":"tom", "age": 18}
sm["002"] = {"name": "jack", "age": 20}
# 查看学生人数
print(len(sm))
# 删除"001"号学生
del sm["001"]
# 检查是否删除
print(sm["001"])
print(sm["002"])
运行结果:
2
None
{'name': 'jack', 'age': 20}
分析:在自定义对象可以使用 对象[ ] 形式来直接操作对象中的容器,使代码书写更加简洁,可读性更高。
7.__str__ 方法
__str__ 的作用是一个自定义的的类型,在转换成字符串类型,程序是不知道怎么办的的,因为程序也不知道你倒底写了什么。
python提供 __str__ 这个方法,让开发人员重写这个方法,制定自定义对象转换成字符串的规则。
8.小结:
- 魔法属性和魔法方法都是 Python 定义好的一些属性或方法,用来实现一些特定的功能。
- 魔法属性和魔法方法的主要作用是用来简化 Python 的操作,在编写代码时让使用方式更加简洁。
三、多重继承和多继承
1.继承概述:
在面向对象程序开发中,继承是一个非常重要的概念。也是一个非常贴近现实继承关系的一种思想。
通过继承,可以让子类去拥有父类中的属性和方法,而不必重新编写相同的代码,并且可以在父类的基础上添加新的属性和功能。
在继承的同时,子类还可以重写父类中的方法,从而获取与父类不同的功能,实现多态。
在 Python 中 所有的类都是存在继承关系的,没有显示继承的,都是默认继承 object 类。
2.继承的作用:
-
子类在继承父类的同时可以向子类中添加新的属性和功能,提高开发效率,避免代码冗余。
-
实现多态。
3.单重单继承:
通过继承,让子类调用方法,如果子类存在就直接调用执行,如果没有,就会到父类中去查找,如果父类中有就执行父类中的方法,如果没有就再去父类中去查找,如果父类中没有就会报错。
父类虽然没有显示的去书写继承了哪个类,实际上在解释器执行时,会默认继承 object 类。
4.多重单继承的初始化问题:
在设计单类时,属性初始化,只需要重写__init__方法即可,可是当有多个类存在继承关系时,类中的属性是怎样初始化的呢?
代码实例:
#!/usr/bin/env python3
# coding=utf-8
class Person(object):
"""定义一个Person类"""
def __init__(self, name):
self.__name = name
print("Person init...")
def get_name(self):
return self.__name
class Father(Person):
"""定义Father类继承Person类"""
def __init__(self, name, age):
Person.__init__(self, name)
self.__age = age
print("Father init...")
def get_age(self):
return self.__age
class Son(Father):
"""定义Son类继承Father类"""
def __init__(self, name, age, gender):
Father.__init__(self, name, age)
self.__gender = gender
print("Son init..")
def get_gender(self):
return self.__gender
运行结果:
Person init...
Father init...
Son init..
小明 18 男
分析:从运行结果看出,子类会继承父类的属性和方法。初始化是,子类只需要初始化自己扩展的属性即可,父类的属性交给父类自己去初始化。使用 父类名.__init__() 的形式来调用父类的初始化方法。
原因:
- 父类中的属性是私有的,子类根本不知道父类里的属性是什么。
- 子类也不知道父类在初始化操作过程中做了哪些工作。
- 所以谁的内容就交给谁去执行是最安全的。
5.多重多继承的初始化问题:
在python中,允许一个类同时继承多个类。
扩展(了解):
C语言是不是面向对象语言,不存在继承;
C++:允许继承多个类,即子类与父类之间是派生关系。继承关系有三种:公有继承、保护继承、私有继承;
java:以你为多继承在实现上是不安全的,即,父类中可能存在同名但功能不同的方法,所以java中不存在多继承关系,但是,java中可以使用“单继承多实现”的方式实现多继承,即,只继承一个父类,但实现多个接口的方式,重写接口中的抽象方法,进行数据的有效性控制;
C#:大体上同java
python的多继承代码实例:
#!/usr/bin/env python3
# coding=utf-8
class Person():
def __init__(self, name):
self.__name = name
print('Peron init...')
def get_name(self):
return self.__name
class Mother(Person):
def __init__(self, name, job):
Person.__init__(self, name)
self.__job = job
print('Mother init...')
def get_job(self):
return self.__job
class Father(Person):
def __init__(self,name, age):
Person.__init__(self, name)
self.__age = age
print('Father init...')
def get_age(self):
return self.__age
class Son(Mother, Father):
def __init__(self,name, age, gender, job):
Mother.__init__(self, name, job)
Father.__init__(self, name, age)
self.__gender = gender
print('Son init...')
def get_gender(self):
return self.__gender
if __name__ == '__main__':
Tom = Son('Tom', 18, '男','老师')
print(Tom.get_name(),Tom.get_age(),Tom.get_gender(),Tom.get_job())
运行结果:
Peron init...
Mother init...
Peron init...
Father init...
Son init...
Tom 18 男 老师
分析:
通过程序的结果来看,虽然程序可以执行,但是 Person 类的 __init__ 方法被执行了两次。
这种初始化方式被称为钻石继承(菱形继承)。
很明显这是不对的,因为 Father 类和 Mother 类都是继承于 Person 类的,在自己的初始化方法中,执行了父类的初始化方法,所以执行了两次。从开发的角度来分析,Person 代码执行是冗余的。这是不符合开发规则的。
其次,python之所以可以实现多继承,是python默认的程序员知道自己的行为,对不同类中可能出现的同名方法或属性,程序员自行做了处理,python默认是安全的。
6.super函数:
在 Python 中,提供 Super 函数来解决多继承时父类被重复初始化这个问题。
super函数的格式:
super(CurrentClassName, self).__init__(*args, **kwargs)
代码实现:
#!/usr/bin/env python3
# coding=utf-8
class Person():
def __init__(self, name):
self.__name = name
print('Peron init...')
def get_name(self):
return self.__name
class Father(Person):
def __init__(self, age, *args):
super(Father, self).__init__(*args)
self.__age = age
print('Father init...')
def get_age(self):
return self.__age
class Mother(Person):
def __init__(self, job,*args):
super(Mother, self).__init__(*args)
self.__job = job
print('Mother init...')
def get_job(self):
return self.__job
class Son(Father, Mother):
def __init__(self, name, age, gender, job):
super(Son, self).__init__(age, job,name)
self.__gender = gender
print('Son init...')
def get_gender(self):
return self.__gender
if __name__ == '__main__':
tom = Son('Tom', 18, '男','老师')
print(tom.get_name(),tom.get_age(),tom.get_gender(),tom.get_job())
运行结果:
Peron init...
Mother init...
Father init...
Son init...
Tom 18 男 老师
分析:
由运行结果可以看出,Person只初始化了一次。那么问题又来了,为什么使用super方法之后就可以正常初始化呢?在初始化时,时以什么为依据的呢?
7.__mro__魔法属性:
7.1__mro__概述:
__mro__:(Method Resolution Order):即方法解析顺序是python中用于处理二义性问题的算法,解析顺序在类出现继承时,每个类中都会保存一个当前类的继承关系的表(详情了解“C3算法”)。
7.2 super方法的实现过程:
super 函数在执行时,会在自己保存的这个继承关系中去查找第一个参数,也就是当前的类名的下一个类是谁。然后去调用下个类的初始化方法。Python 中使用深度优先算法来决定继承关系的排序。
如图:
print(Son.__mro__)
(<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class '__main__.Person'>, <class 'object'>)
7.3 深度优先算法与广度优先算法:
深度优先算法:
(1)访问初始顶点v并标记顶点v已访问。
(2)查找顶点v的第一个邻接顶点w。
(3)若顶点v的邻接顶点w存在,则继续执行;否则回溯到v,再找v的另外一个未访问过的邻接点。
(4)若顶点w尚未被访问,则访问顶点w并标记顶点w为已访问。
(5)继续查找顶点w的下一个邻接顶点wi,如果v取值wi转到步骤3。直到连通图中所有顶点全部访问过为止
总结来说:深度优先先按照一棵子树开始遍历,当找不到可遍历元素时进行回溯,再遍历下一课子树,直至全部元素都被访问一遍。
广度优先算法:
(1)顶点v入队列。
(2)当队列非空时则继续执行,否则算法结束。
(3)出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
(4)查找顶点v的第一个邻接顶点col。
(5)若v的邻接顶点col未被访问过的,则col入队列。
(6)继续查找顶点v的另一个新的邻接顶点col,转到步骤5。直到顶点v的所有未被访问过的邻接点处理完。转到步骤2。
总结来说:广度优先是逐层遍历,同级元素被遍历完成后,再进入下一级,遍历子级元素,直至所有元素被访问完成。
8.super的简化写法:
在初始化父类时,也可以使用 super().__init__() 函数来调用,简化super函数的写法。
这时,super函数中没有参数,还是能正确执行,就是依照 __mro__ 中的顺序来执行的。
代码实现:
#!/usr/bin/env python3
# coding=utf-8
class Person():
def __init__(self, name):
self.__name = name
print('Peron init...')
def get_name(self):
return self.__name
class Mother(Person):
def __init__(self, name, age, job):
# super(Mother, self).__init__(name, age)
super().__init__(name,age)
self.__job = job
print('Mother init...')
def get_job(self):
return self.__job
class Father(Person):
def __init__(self,name, age):
# super(Father, self).__init__(name)
super().__init__(name)
self.__age = age
print('Father init...')
def get_age(self):
return self.__age
class Son(Mother, Father):
def __init__(self, name, age, gender, job):
# super(Son, self).__init__(name, age, job)
super().__init__(name,age,job)
self.__gender = gender
print('Son init...')
def get_gender(self):
return self.__gender
if __name__ == '__main__':
tom = Son('Tom', 18, '男','老师')
print(tom.get_name(),tom.get_age(),tom.get_gender(),tom.get_job())
对象在调用方法时,也会依照这个顺序来查找方法。
类在继承时,继承关系的书写顺序会影响 __mro__ 中的顺序。
代码:
#!/usr/bin/env python
# coding=utf-8
"""继承关系"""
class A(object):
"""定义一个基类"""
pass
class B(A):
"""B类继承A类"""
pass
class C(A):
"""C类继承A类"""
pass
class D(B, C):
"""D类继承B类和C类"""
pass
class H(object):
# 定义一个基类H类
pass
class E(H):
# E类继承H类
pass
class G(H):
# G类继承H类
pass
class F(D, E, G):
# F类继承D类,E类,G类
pass
if __name__ == '__main__':
print(F.__mro__)
与运行结果:
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.G'>, <class '__main__.H'>, <class 'object'>)
9.小结:
- 继承的思想是为了避免代码的冗余,方便程序的扩展和复用
- 多重继承是继承关系中很多代(纵向)
- 多继承是一个类可以同时继承多个类(横向)
- Python 中的类可以多重继承,也可以多继承
- Python 通过 __mro__ 表来确定继承的顺序,使用的是深度优先算法