python-继承_继承的实现原理_Mixins机制

继承

1. 继承介绍

1. 继承的概念

继承是一种新建类的方式 , 新建的类称之为子类 , 被继承的类称之为父类/基类/超类

python 支持多继承

2. 继承的目的

子类会遗传父类的属性 , 所有继承是用来解决类与类之间代码冗余的问题

3. 继承的实现

要找出类与类之间的继承关系 , 需要先抽象 , 再继承 . 抽象即总结相似之处 , 总结对象之间的相似之处得到类 , 总结类与类之间的相似之处就可以得到父类 , 基于抽象的结果 , 我们就找到了继承关系

class Parent1:
    pass

class Parent2:
    pass

class Sub1(Parent1):
    pass

class Sub2(Parent1,Parent2):
    pass

# 通过类的内置属性 __bases__ 可以查看类继承的所有父类
print(Sub1.__bases__)
print(Sub2.__bases__)

# 继承案例
class OldboyPeople:
    school = "oldboy"
    
class Student(OldboyPeople):
    def __init__(self,name,age,gender,stud_id,course):
        self.name = name
        self.age = age
        self.gender = gender
        self.stud_id = stud_id
        self.course = course
        
    def choose(self):
        print("%s 正在选课" % self.name)

class Teacher(OldboyPeople):
    def __init__(self,name,age,gender,salary,level):
        self.name = name
        self.age = age
        self.gender = gender
        self.salary = salary
        self.level = level
        
    def score(self,stu,num):
        stu.num = num

stu1 = Student("艾利克斯",73,"male",1001,"python全栈开发")
tea1 = Teacher("egon",18,"male",2000,10)
        
print(stu1.school)        

2. 在子类派生的新方法中重用父类的功能

1. 方式一: 指名道姓地调用某一个类的函数

特点: 不依赖于继承关系

class OldboyPeople:
    school = "oldboy"
    #          空对象,"艾利克斯",73,"male"
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
        
    def f1(self):
        print("11111111")
        
class Student(OldboyPeople):
    #          空对象,"艾利克斯",73,"male",1001,"python全栈开发"
    def __init__(self,name,age,gender,stu_id,course):
        # OldboyPeople.__init__(空对象,"艾利克斯",73,"male")
        OldboyPeople.__init__(self,name,age,gender)
        self.stu_id = stu_id
        self.course = course
        
    def choose(self):
        print("%s 正在选课" %self.name)
        
    def f1(self):
        OldboyPeople.f1(self)
        print("222222222")
        
class Teacher(OldboyPeople):
    def __init__(self,name,age,gender,salary,level):
        OldboyPeople.__init__(self,name,age,gender)
        self.salary = salary
        self.level = level
    
    def score(self,stu,num):
        stu.num = num
        
stu1 = Student("艾利克斯",73,"male",1001,"python全栈开发")
tea1 = Teacher("egon",18,"male",2000,10)
stu1.f1()

3. 属性查找

有了继承关系 , 对象在查找属性时 , 先从对象自己的__dict__中找 , 如果没有则去子类中找 , 然后再去父类中找

**1. 由对象发起的属性查找 **

会从对象自身的属性里检索 , 没有则会按照对象的类.mro()规定的顺序依次找下去

**2. 由类发起的属性查找 **

会按照当前类.mro()规定的顺序依次找下去

# 案例1
# obj.f1() 会在父类Foo中找到f1,先打印Foo.f1,然后执行self.f2(),即obj.f2()
# 按照: 对象本身 -> 类bar -> 父类Foo 的顺序依次找下去,在类Bar中找到f2,因而打印结果为Bar.f2
class Foo:
    def f2(self):
        print("Foo.f2")
        
    def f1(self):
        print("Foo.f1")
        self.f2() # obj.f2()
        
class Bar(Foo):
    def f2(self):
        print("Bar.f2")
        
obj = Bar()
obj.f1()

# 案例2
# 父类如果不想让子类覆盖自己的方法,可以在方法名字前加前缀 __ , 将方法设置为私有的
class Foo:
    def __f2(self): # _Foo__f2
        print("Foo.f2")
        
    def f1(self):
        print("Foo.f1")
        self.__f2() # obj._Foo__f2()
        
class Bar(Foo):
    def __f2(self): # _Bar__f2
        print("Bar.f2")
        
obj = Bar()
obj.f1()

4. 继承的实现原理

1. 菱形问题

一个子类继承的多条分支最终汇聚到一个非object类

2. 菱形继承下属性查找方式

在菱形继承下,新式类与经典类关于属性查找的方式不同

1.新式类: 广度优先

2.经典类: 深度优先

# 例1: 非菱形继承,经典类与新式类的属性查找顺序都一样
class E:
    def test(self):
        print("from E")
    pass    

class F:
    def test(self):
        print("from F")
        
class B(E):
    def test(self):
        print("from B")
    pass

class C(F):
    def test(self):
        print("form C")
        
class D:
    def test(self):
        print("from D")
       
class A(B,C,D):
    def test(self):
        print("from A")
     
obj=A()
obj.test()

# 例2: 菱形继承
class G(object): # 在python2中,未继承object的类及其子类,都是经典类
    def test(self):
        print("from G")
    pass

class E(G):
    def test(self):
        print("from E")
        
class F(G):
    def test(self):
        print("from F")
        
class B(E):
    def test(self):
        print("from B")
        
class C(F):
    def test(self):
        print("from C")
        
class D(G):
    def test(self):
        print("from D")
        
class A(B,C,D):
    def test(self):
        print("from A")
        
obj=A()
obj.test()

print(A.mro())
"""
[
<class '__main__.A'>, <class '__main__.B'>, 
<class '__main__.E'>,<class '__main__.C'>,
<class '__main__.F'>, <class '__main__.D'>, 
<class '__main__.G'>, <class 'object'>
]
"""

5. 补充知识

1. 新式类 : 但凡是继承了object类的子类 , 该子类子子孙孙类都称之为新式类

2. 经典类 : 没有继承object类的子类 , 该子类子子孙孙类都称之为经典类

3. python3 中全都是新式类 , python2 中才有经典类

4. 在python3 中没有继承任何类的类会默认继承object类

class Foo(object):
    pass

print(Foo.__bases__) # <class 'object'>

5. MRO (method resolution order) 方法解析顺序

事实上 , 程序中定义的每个类 , python都会计算出一个方法解析顺序 , 代表类继承的顺序

6. MRO列表的顺序是通过C3线性化算法来实现的 , MRO列表就是合并所有父类的列表

7. MRO列表的特点

  1. 子类永远在父类的前面

  2. 如果有多个父类 , 会根据它们在列表中的顺序去检查

  3. 如果对下一个类存在两种不同的合法选择 , 那么选择第一个父类

6.python Mixins机制

1. 继承关系 : is-a
2. Mixins机制

Mixins机制指的是子类混合(mixin)不同类的功能 , 而这些类采用统一的命名规范(例如Mixin后缀) , 以此标识这些类知识用来混合功能的 , 并不是用来标识子类的从属"is-a"关系的 , 所有Mixins机制本质仍是多继承 , 但同样遵守"is-a"关系

Mixins机制的命名方式就是用来明确告诉别人,这个类是作功能添加到子类中 , 而不是作为父类

3. 使用Mixin类实现多重继承的注意点

1. 该类必须表示某一种功能 , 而不是某个物品 , python 对于mixin类的命名方式一般以 Mixin , able , ible 为后缀

2. 该类必须责任单一 , 如果有多个功能 , 那就写多个Mixin类 , 一个类可以继承多个Mixin , 为了保证遵循继承的"is-a"原则 , 只能继承一个标识其归属含义的父类

3. Mixin类不依赖于子类的实现

4. 子类即便没有继承Mixin类 , 也照样可以工作 , 就是缺少了某些功能

7. 单继承

如果一个类继承另外一个类 , 该类叫做子类(衍生类),被继承的类叫做父类(基类,超类)

继承: (1) 单继承 (2) 多继承

class Human(object):
	hair = "黑色"
	sex = "男"
	
	def cry(self):
		print("人类在伤心的时候,会留下鳄鱼的眼泪")
		
	def eat(self):
		print("人类在远古时候,抓到猎物直接吃")

	def __makebaby(self):
		print("人类在发育到成熟的时候,会造人")

# (1) 子父继承之后,子类可以调用父类的公有成员
class Man(Human):
	pass

obj = Man()
print(obj.hair)
obj.cry()

# (2) 子父继承之后,子类不能调用父类的私欲成员
class Woman(Human):
	def pub_func(self):
		self.__makebaby()

obj = Woman()
# obj.__makebaby() error
# obj.pub_func() error 

# (3) 子父继承之后,子类可以改写父类中的方法
"""
子父继承之后
如果子类里面有该成员属性或者方法,优先调用自己的
如果没有该成员,调用父类中的成员
如果都没有,直接报错
"""
class Children(Human):
	sex = "女性"
	def cry(self):
		print("小孩只会哇哇哇的哭")

obj = Children()
obj.cry()
print(obj.__dict__)
print(Children.__dict__)

8. 多继承

# (1) 基本语法
class Father():
	property = "风流倜傥,一表人才,一枝梨花压海棠"
	def f_hobby(self):
		print("社会摇,蹦迪,吃喝嫖赌,大保健")

class Mother():
	property = "闭月羞花,倾国倾城,一枝红杏出墙来"
	def m_hobby(self):
		print("打麻将,打牌,喝酒抽烟烫头,跳广场舞")

class Daughter(Father,Mother):
	pass

# 实例化对象
obj = Daughter()
print(obj.property)
obj.m_hobby()

# (2) super用法
"""
(1)super本身是一个类 super()是一个对象 用于调用父类的绑定方法
(2)super() 只应用在绑定方法中,默认自动传递self对象 (前提:super所在作用域存在self)
(3)super用途: 解决复杂的多继承调用顺序	
"""
class Father():
	property = "风流倜傥,一表人才,一枝梨花压海棠"
	def f_hobby():
		print("社会摇,蹦迪,吃喝嫖赌,大保健")

class Mother():
	property = "闭月羞花,倾国倾城,一枝红杏出墙来"
	def m_hobby(self):
		print("打麻将,打牌,喝酒抽烟烫头,跳广场舞")

class Son(Father,Mother):
	property = "喜欢打游戏,wow,lol,dnf,泡泡堂,cf,dota,大话西游,跑跑卡丁车,吃鸡"		
	
	# 1.利用类来调用父类的成员
	def skill1(self):	
		Father.f_hobby()
		print(Mother.property)

	# 2.利用对象调用父类的成员
	def skill2(self):
		self.m_hobby()
		print(self.property)
		
	# 3.利用super调用父类的属性和方法
	"""
		super()只调用父类的相关公有成员,不会调用自己的本类成员,父类没有直接报错.
		super()在调用父类方法时,只调用父类的绑定方法,默认传递参数是本类的对象self
	"""
	def skill3(self):
		# print(super())
		print(super().property)
		super().m_hobby()
		# super().f_hobby() error

obj = Son() 
print("<===============>")
obj.skill1()
print("<===============>")
obj.skill2()		
print("<===============>")
obj.skill3()

"""
self 和 super()的区别
self    在调用成员时,先看看自己的类对象中是否存在该成员,如果有调用自己的,如果没有,调用父类的.如果都没有报错
super() 在调用成员时,只调用父类的相关成员(属性,绑定方法),永远不会调用自己的.如果父类没有,直接报错.
"""

9. 菱形继承

"""
	 Human
Man          Woman
	Children
"""
class OldWoman():
	pass

class Human():
	pty = 4
	def feelT(self):
		print("原始人类如果热了,脱皮1")
		print(self.pty)
		print("原始人类如果冷了,扒别人的皮2")
	
class Man(Human):
	# pty = 3
	def feelT(self):
		print("现代男人如果热了,脱衣服,脱裤子3")
		super().feelT()
		print("现代男人如果冷了,脱别人的衣服,脱别人的裤子4")
		
class Woman(Human):
	# pty = 2
	def feelT(self):
		print("现代女人如果热了,吹空调,吃雪糕5")
		super().feelT()
		print("现代女人如果冷了,喝热水,用暖宝宝6")
	
class Children(Man,Woman):
	# pty = 1
	def feelT(self):
		print("现代小孩如果热了,就哭,嗯哭7")
		super().feelT()
		print("现代小孩如果冷了,也要哭8")
		
obj = Children()
obj.feelT()
# 73512648

# mro 列表 : super用途的一个体现.解决复杂的多继承调用顺序关系
"""
类.mro()  返回的是方法调用顺序列表,针对于多继承下的同名方法,按照顺序依次的进行调用
"""
lst = Children.mro()
print(lst)
"""
[
<class '__main__.Children'>, 
<class '__main__.Man'>, 
<class '__main__.Woman'>, 
<class '__main__.Human'>, 
<class 'object'>
]

"""
# issubclass 判断子父关系 (应用在类当中,判断子父关系)
"""只要在一条继承链上即可(有血缘关系)"""
res = issubclass(Children,Man)
print(res)
res = issubclass(Children,Woman)
print(res)
res = issubclass(Children,Human)
print(res)
res = issubclass(Children,(Human,Woman,Man,OldWoman))
print(res)
res = issubclass(Children,OldWoman)
print(res)


# isinstance (应用在对象和类之间,判断类型)
"""只要在一条继承链上即可(有血缘关系)"""
res = isinstance(obj,Children)
print(res)
res = isinstance(obj,Human)
print(res)
res = isinstance(obj,(Human,Children,Woman))
print(res)
res = isinstance(obj,OldWoman)
print(res)

10. _init_ 魔术方法

_init_ 魔术方法 (构造方法)

触发时机: 实例化对象,初始化的时候触发

功能:为对象添加成员

参数:参数不固定,至少一个self参数

返回值:无

# (1) 基本语法
class MyClass():
	def __init__(self):
		print("初始化方法被触发")
		# 为当前对象self 添加成员 name
		self.name = "袁伟倬"

# 实例化对象 
obj = MyClass()
print(obj.name)

# (2) 带有多个参数的构造方法
class MyClass():
	def __init__(self,name):	
		# self.成员名 = 参数
		self.name = name
# 实例化对象
obj = MyClass("宋云杰") # 在实例化对象时候,给构造方法传递参数
print(obj.name)

# (3) 类可以是一个,对象可以是多个
"""一个类可以实例化多个不同的对象,而对象和对象之间彼此独立,但都可以使用类中的公有的成员"""
class Children():
	def __init__(self,name,skin):
		self.name = name
		self.skin = skin
		
	def cry(self):
		print("小孩一出生就哇哇哇的哭")
		
	def drink(self):
		print("小孩一下生就要喝奶奶")
	
	def __la(self):
		print("小孩拉粑粑是私有的")
		
	def pub_info(self):
		print("该对象的名字是{},该对象的肤色是{}".format(self.name,self.skin))
	
# 创建第一个小孩
afanda = Children("阿凡达","蓝色的")
afanda.pub_info()
afanda.cry()

# 创建第二个小孩
afanti = Children("阿凡提","黄色的")
afanti.pub_info()
afanti.drink()

# 创建第三个小孩
bao = Children("我滴宝强","绿色的")
bao.pub_info()
bao.__la() # 无法在类外调用私有的成员
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I believe I can fly~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值