11 继承、多继承、魔术方法
-
上节课作业解答
class Rectangle(object): def __init__(self, width, length): self.width = width self.length = length # 两个方法之间留一个空行 # 运算符后面跟一个空格 def get_area(self): return self.width * self.length #类之间空两行 rect1 = Rectangle(10, 20) rect1.get_area() print(rect1.get_area()) # 运行结果: 200
一,继承
(一)继承的使用方式
class Rectangle(object): #父类/基类 def __init__(self, width, length): self.width = width self.length = length def get_area(self): return self.width * self.length class Square(Rectangle): #继承 pass square = Square(50,50) print(square.get_area()) # 运行结果: 2500 # 此处Square类继承了Rectangle类 Square拥有了Rectangle里的所有方法及其属性 # 重用代码,方便代码的管理
(二)继承的搜索
class Rectangle(object): def __init__(self, width, length): # 实例化传参时 将初始化参数 self.width = width self.length = length def get_area(self): return self.width * self.length class Square(Rectangle): def __init__(self, width, length): if width == length: Rectangle.__init__(self, width, length) # 此处调用了父方法、这个self是正方形类的实例,不是矩形类的实例。 super().__init__(width,length) # 第二种调用父类的方法 else: print('长度和宽度不相等,不能成为正方形') square = Square(25, 25) print(square.get_area()) square1 = Square(25, 22) # 运行结果 625 长度和宽度不相等,不能成为正方形
(三) __bass__ 特殊属性
-
Object最顶层的类,类的老祖宗
-
__base__ 查看继承的父类
-
__bases__ 查看继承的全部父类
# 使用方法: Rectangle.__bases__ 实例.__class__.bases__
二,多继承
-
一个子类可以继承多个父类是多继承
-
一层层继承下去是多重继承
# 例1 class Base(object): def func(self): print('这是base类') def func_base(self): print('这是func_base类') class A(Base): def func(self): print('这是a类') super().func() def func_a(self): print('这是func_a类') class B(Base): def func(self): print('这是b类') super().func() def func_b(self): print('这是func_b类') class C(A, B): def func(self): print('这是c类') super().func() d = C() d.func() print(C.__mro__) # 运行结果: 这是c类 这是a类 这是b类 这是base类 (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>) # 说明 : # 1,C类继承了A类及B类的属性及方法 # 2,对于A与B相同的方法func,优先继承最左边的那个
#例2 class Base(object): def func(self): print('这是base类') class Base2(object): def func(self): print('这是base2类') def func_base2(self): print('这是func_base2类') class A(Base): def func(self): print('这是a类') super().func() def func_a(self): print('这是func_a类') class B(Base2): def func(self): print('这是b类') super().func() def func_b(self): print('这是func_b类') class C(A, B): def func(self): print('这是c类') super().func() d = C() d.func() print(C.__mro__) 运行结果: 这是c类 这是a类 这是base类 (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.Base'>, <class '__main__.B'>, <class '__main__.Base2'>, <class 'object'>)
多继承 调用父类重写方法
当子类重写父类方法之后,子类如果想再次调用父类的方法,可以使用这两种方法
class C(A,B): def func(self): print('这是c') super().func() #通过super调用 class C(A,B): def func(self): print('这是c类') A.func(self) #通过父类名字直接调用 d = C() d.func() 打印结果: 这是c类 这是a类 这是b类 这是base类 class C(A,B): def func(self): print('这是c类') B.func(self) #通过父类名字直接调用 d = C() d.func() 打印结果: 这是c类 这是b类 这是base类
使用 super调用父类重名方法,可以通过类的__mro__属性来查看多继承的情况下,子类调用父类方法时,在父类中的搜索顺序
# 有两个用法,第一个通过类来查询,第二个通过对象来查询 C.mro() # 方式一 :类名.mro() d.__class__.mro # 方式二:对象.__class__.__mro__ (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
-
继承多个父类时,若想指定继承哪一个类的方法,可以重写的方式达到效果
#例3 class C(A, B): # 继承A,B def func(self): #覆盖父类 B.func(self) # 指定继承B类内的func方法
-
super()
# 例4 class Base(object): def play(self): print('Base is playing!') class A(Base): # 继承Base def play(self): # 自动覆盖父类的此方法 super().play() # 调用父类方法 print('A is playing!') a = A() a.play() # 运行结果: Base is playing! A is playing! #说明: super()可自动找到父类方法
-
类.mro() 查看继承顺序
-
类在生成时会自动生成方法解析顺序,可以通过 类名.mro来查看
print(C.mro()) # 运行结果: [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
-
super()深层次用法
-
super函数可以来调用父类的方法,使用super的好处在于即使父类改变了,那么也不需要更改类中的代码
# 例5 class Base(object): def play(self): print('Base is playing!') class A(Base): # 继承Base def play(self): # 自动覆盖父类的此方法 super().play() print('A is playing') class B(Base): # 继承Base def play(self): super().play() print('B is playing') class C(A, B): # 继承A,B def play(self): super().play() print('C is playing') c = C() c.play() print(C.mro()) # 运行结果: Base is playing! B is playing A is playing C is playing [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>] # 说明 循根溯源执行 由mro()可以看出,C的继承顺序为A,B,Base,object c在执行play()方法时,也是按此顺序执行 1,c.play()遇到super().play(),此时压栈并溯源到A.play() 2,在A.play()遇到super().play(),此时压栈并继续溯源到B.play() 3,在B.play()遇到super().play(),此时压栈并继续溯源到Base.play() 4,在Base.play()遇到print,此时执行打印: 1# Base is playing! 5,Base.play()执行结束,此时出栈到B.play(),在此时B中的super().paly()已经执行完成, 紧接着遇到print,此时执行打印: 2# B is playing 6,B.play()执行结束,此时继续出栈到A.play(),在此时A中的super().paly()已经执行完成, 紧接着遇到print,此时执行打印: 3# A is playing 7,A.play()执行结束,此时继续出栈到C.play(),在此时C中的super().paly()已经执行完成, 紧接着遇到print,此时执行打印: 4# C is playing 8,运行结束。输出如上结果。
三,魔术方法(下节课内容)
(一)类的专有方法(目前无须理解):
方法 | 作用 |
---|---|
__init__ |
构造函数,在生成对象时调用 |
__del__ |
析构函数,释放对象时使用 |
__repr__ |
打印,转换 |
__setitem__ |
按照索引赋值 |
__getitem__ |
按照索引获取值 |
__len__ |
获得长度 |
__cmp__ |
比较运算 |
__call__ |
函数调用 |
__add__ |
加运算 |
__sub__ |
减运算 |
__mul__ |
乘运算 |
__div__ |
除运算 |
__mod__ |
求余运算 |
__pow__ |
乘方 |
(二)魔术方法之运算方法
add(self,other) | x+y |
---|---|
sub(self,other) | x-y |
mul(self,other) | x*y |
mod(self,other) | x%y |
iadd(self,other | x+=y |
isub(self,other) | x-=y |
radd(self,other) | y+x |
rsub(self,other) | y-x |
imul(self,other) | x*=y |
imod(self,other) | x%=y |
(三)魔术方法: __init__(self)
class Person(object): def __init__(self, name, age, height): self.name = name self.age = age self.height = height def eat(self): print('%s正在吃饭...' % self.name) p1 = Person('同学A', 18, 188) p1.eat() print(p1.name) print(p1.age) print(p1.height) # 运行结果: 同学A正在吃饭... 同学A 18 188
-
运行
p1 = Person('同学A', 18, 188)
过程:
注释 | 过程 |
---|---|
实例化,产生一个类的实例 | p1 = Person('同学A', 18, 188) |
python自动调用 实例.__init__(参数) |
p1.__init__('同学A', 18, 188) |
转换为 类.__init__(实例,参数) |
Person.__init__(p1,'同学A', 18, 188) |
(四)魔术方法__add__ :当类的实例之间使用 + 号时,会自动调用__add__这个魔术方法
class Add(object): def __init__(self,str1): self.str1 = str1 def __add__(self,other): return self.str1 + other.str1 a = Add('ab') b = Add('cd') print(a + b) 打印结果: abcd
(五)字符串表示 __str__ 和 __repr__
__str__和__repr__原理:
在python中,repr和str这两个方法都是用于显示的,str是面向用户的,而
repr面向程序员。
使用内置函数str和repr方法在处理对象的时候,分别调用的是对象的str和repr方法
调用str函数来处理输出的对象,如果对象没有定义str方法,则调用repr方法
调用repr函数来处理输出的对象,则调用repr处理,
使用print操作 会首先尝试 调用str方法 ,如果str方法没有定义,则调用repr方法
在交互模式下,输入对象 显示对象 repr方法的返回值
例1: __str__和__repr__ >>> class Rectangle: def __init__(self,length,width): self.length = length self.width = width def __str__(self): return '矩形的长:%s,矩形的宽:%s' % (self.length, self.width) def __repr__(self): return '长:%s, 宽: %s' % (self.length, self.width) >>> a = Rectangle(10,8) >>> str(a) '矩形的长:10,矩形的宽:8' >>> repr(a) '长:10, 宽: 8'
例子2:只写了__str__魔术方法 交互模式输入:a ,返回的是一个对象 print(a):打印__str__魔术方法,返回的是内容 >>> class Rectangle: def __init__(self,length,width): self.length = length self.width = width def __str__(self): return '矩形的长:%s,矩形的宽:%s' % (self.length, self.width) >>> a = Rectangle(10,8) >>> a <__main__.Rectangle object at 0x05D8FE90> >>> print(a) 矩形的长:10,矩形的宽:8
例子3:只写了__repr__魔术方法 交互模式输入 a :返回的是调用__repr__魔术方法,返回的是内容 print(a): 打印调用__repr__魔术方法,返回的是内容 >>> class Rectangle: def __init__(self,length,width): self.length = length self.width = width def __repr__(self): return '长:%s, 宽: %s' % (self.length, self.width) >>> a = Rectangle(10,8) >>> a 长:10, 宽: 8 >>> print(a) 长:10, 宽: 8
例子4:__str__ 和 __repr__ 魔术方法 都写上 >>> class Rectangle: def __init__(self,length,width): self.length = length self.width = width def __str__(self): return '矩形的长:%s,矩形的宽:%s' % (self.length, self.width) def __repr__(self): return '长:%s, 宽: %s' % (self.length, self.width) >>> a = Rectangle(10,8) >>> a 长:10, 宽: 8 >>> print(a) 矩形的长:10,矩形的宽:8
说明:
对使用者使用友好的是__str__
向使用者提供尽可能简洁且有用的信息。让用户尽可能吸收到必要的信息。
对开发者调试友好的是__repr__
向开发者提供接近创建时的信息。让开发者可以直接通过复制粘贴来重建对象。
(六) __call__方法:让类的实例可以像函数一样被调用。
>>> class Rectangle: def __init__(self,length,width): self.length = length self.width = width def __call__(self): return '调用了__call__方法' >>> a = Rectangle(10,8) >>> a() '调用了__call__方法'
(七) 类中的一些查询相关信息的方法 (了解既可)
1、__class__ # 查看类名 # 格式:实例.__class__ 2、__dict__ # 查看全部属性,返回属性和属性值键值对形式 # 格式:实例.__dict__ 3、__doc__ # 查看对象文档,即类中(用三个引号引起来的部分) # 格式:类名.__doc 4、__base__ # 查看继承的父类 # 格式:类名.__base__ 5、__bases__ # 查看继承的全部父类 # 格式:类名.__bases__ 6、__mro__ # 查看多继承的情况下,子类调用父类方法时,搜索顺序 # 格式:子类名.__mro__ # 实例.class__.__mro__