Python面向对象的三大特性(继承和多态)
一、Python中的继承
1、什么是继承
我们接下来来聊聊Python代码中的“继承”:类是用来描述现实世界中同一组事务的共有特性的抽象模型,但是类也有上下级和范围之分,比如:生物 => 动物 => 哺乳动物 => 灵长型动物 => 人类 => 黄种人
简单来说,如果一个类A使用了另一个来
2、继承的基本语法
假设A类要继承B类中的所有属性和方法(私有属性和私有方法除外)
class B(object):
pass
class A(B):
pass
a = A()
a.B中的所有公共属性
a.B中的所有公共方法
案例:Person类与Teacher、Student类之间的继承关系
class Person(object):
def eat(self):
print('需要吃食物')
def drink(self):
print('需要喝水')
class Teacher(Person):
pass
class Student(Person):
pass
teacher = Teacher()
teacher.eat()
teacher.drink()
student = Student()
student.eat()
student.drink()
'''
需要吃食物
需要喝水
需要吃食物
需要喝水
'''
3、与继承相关的几个概念
继承:一个类从另一个已有的类获得其成员的相关特性,就叫作继承!
派生:从一个已有的类产生一个新的类,称为派生!
很显然,继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的!
父类:也叫作基类,就是指已有被继承的类!
子类:也叫作派生类或扩展类
扩展:在子类中增加一些自己特有的特性,就叫作扩展,没有扩展,继承也就没有意义了!
单继承:一个类只能继承自一个其他的类,不能继承多个类,单继承也是大多数面向对象语言的特性!
多继承:一个类同时继承了多个父类
4、单继承
单继承:一个类只能继承自一个其他的类,不能同时继承多个类。这个类会有具有父类的属性和方法。
示例:
# 定义一个共性类(父类)
class Person(object):
pass
# 定义一个个性类(子类)
class Student(Person):
pass
案例:比如电脑可以分为两种类型(笔记本电脑、台式电脑)
# 1、定义一个共性类(父类)
class Computer(object):
def play(self):
print('可以玩游戏')
class laptops(Computer):
pass
class desktop(Computer):
pass
HUAWEI = laptops()
HUAWEI.play()
'''
可以玩游戏
'''
5、单继承特性(多层继承):传递性
在Python继承中,如A类继承了B类,B类又继承了C类。则根据继承的传递性,则A类也会自动继承C类中所有属性和方法(公共)
示例:
class C(object):
def func(self):
print('我是C类中的相关方法func')
class B(C):
pass
class A(B):
pass
a = A()
a.func()
'''
我是C类中的相关方法func
'''
6、多继承
Python语言是少数支持多继承的一门编程语言,所谓的多继承就是允许一个类同时继承多个类的特性。
示例:
class C(object):
def func1(self):
print('我是C类中的相关方法func')
class B(object):
def func2(self):
print('我是B类中的相关方法func')
class A(C, B):
pass
a = A()
a.func1()
a.func2()
'''
我是C类中的相关方法func
我是B类中的相关方法func
'''
注意:虽然多继承允许我们同时继承自多个类,但是实际开发中,应尽量避免使用多继承,因为如果两个类中出现了相同的属性和方法就会产生命名冲突。
7、子类扩展:重写父类属性和方法
重写也叫作覆盖,就是当子类成员与父类成员名字相同的时候,从父类继承下来的成员会重新定义!
此时,通过子类实例化出来的对象访问相关成员的时候,真正起作用的是子类中定义的成员!
示例:
class Father(object):
属性
方法
class Son(Father):
父类属性和方法
自己的属性和方法(如果子类中的属性和方法与父类中的属性或方法同名,则子类中的属性或方法会对父类中同名的属性或方法进行覆盖(重写))
上面单继承例子中Computer的子类laptops和desktop继承了父类的属性和方法,但是我们笔记本电脑类laptops有自己的特点‘可随身携带’,台式电脑类desktop有自己的特点‘性能强悍’,这时我们需要对父类的feature()方法进行重构。
示例:
# 1、定义一个共性类(父类)
class Computer(object):
def play(self):
print('可以玩游戏')
def feature(self):
print('可以进行计算')
class laptops(Computer):
def feature(self):
print('可以进行计算,并且方便携带')
class desktop(Computer):
def feature(self):
print('可以进行计算,并且性能强悍')
HUAWEI = laptops()
HUAWEI.feature()
Lenove = desktop()
Lenove.feature()
'''
可以进行计算,并且方便携带
可以进行计算,并且性能强悍
'''
注意:重写父类中的feature方法后,此时父类中的feature方法还在,是不过是在其子类中找不到了。
类方法的调用顺序,当我们在子类中重构父类的方法后,laptops子类的实例先会在自己的类 laptops 中查找该方法,当找不到该方法时才会去父类 Computer中查找对应的方法。
8、super()调用父类属性和方法
super():调用父类属性或方法,完整写法:super(当前类名, self).属性或方法(),在Python3以后版本中,调用父类的属性和方法我们只需要使用super().属性或super().方法名()就可以完成调用了。
示例:
# 1、定义一个共性类(父类)
class Computer(object):
def play(self):
print('可以玩游戏')
def feature(self):
print('可以进行计算')
class laptops(Computer):
def feature(self):
super().feature()
print('方便携带')
HUAWEI = laptops()
HUAWEI.feature()
'''
可以进行计算
方便携带
'''
9、MRO属性或MRO方法:方法解析顺序
MRO(Method Resolution Order):方法解析顺序,我们可以通过类名.__mro__或类名.mro()获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找到。
示例:
# 1、定义一个共性类(父类)
class Computer(object):
def play(self):
print('可以玩游戏')
def feature(self):
print('可以进行计算')
class laptops(Computer):
def feature(self):
super().feature()
print('方便携带')
print(laptops.__mro__)
print(laptops.mro())
'''
(<class '__main__.laptops'>, <class '__main__.Computer'>, <class 'object'>)
[<class '__main__.laptops'>, <class '__main__.Computer'>, <class 'object'>]
'''
说明:有MRO方法解析顺序可知,在类的继承中,当某个类创建了一个对象时,调用属性或方法,首先在自身类中去寻找,如找到,则直接使用,停止后续的查找。如果未找到,继续向上一级继承的类中去寻找,如找到,则直接使用,没有找到则继续向上寻找…直到object类,这就是Python类继承中,其方法解析顺序。
综上:object类是所有类的基类(应为这个查找关系到object才终止)
二、Python中的多态
1、什么是多态
多态,指的是一类事物有多种形态。比如:同样一个函数在不同的场景下有不同的状态。
定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。
不同对象 =>使用相同方法=>产生不同的执行结果。
- 多态以来继承(不是必须的)
- 子类方法必须要重写父类方法
首先定义一个父类,其可能拥有多个子类对象。当我们调用一个公共方法(接口)时,传递的对象不同,则返回的结果不同。
好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
2、多态代码实现
多态:可以基于继承也可以不基于继承
示例:
'''
根据多态实现的三个条件,构建一下场景
构建对象对战平台object_play
1 英雄一代战机(战斗力60)与敌军战机(战斗力70)对抗。英雄1代战机失败!
2 卧薪尝胆,英雄二代战机(战斗力80)出场!,战胜敌军战机!
3 对象对战平台object_play, 代码不发生变化的情况下, 完成多次战斗
'''
class HeroFight1(object):
def power(self):
return 60
class HeroFight2(HeroFight1):
# 重写父类方法
def power(self):
return 80
class EnemyFight(object):
# 重写父类方法
def power(self):
return 70
# 搭建一个平台,让双方战机PK
def object_play(herofight1: HeroFight1, enemyfight):
# 注意:此处发生了多态!!!
if herofight1.power() > enemyfight.power():
print('英雄战机胜利, 敌机失败')
else:
print('英雄战机失败, 敌机胜利')
if __name__ == '__main__':
# 实例化一代战机、二代战机和敌机
herofight1 = HeroFight1()
herofight2 = HeroFight2()
enemyfight = EnemyFight()
# 一代战机出场
object_play(herofight1, enemyfight)
# 二代战机出场
object_play(herofight2, enemyfight)
'''
英雄战机失败, 敌机胜利
英雄战机胜利, 敌机失败
'''
3、多态的好处
在不改变框架代码的情况下,通过多态语法轻松的实现模块和模块之间的解耦合;实现了软件系统的可拓展。
-
解耦合:搭建平台def object_play(herofight1: HeroFight1, enemyfight), 相当于任务的调用者;子类、孙子类重写父类的函数,相当于子任务;相当于任务的调用者和任务的编写者进行了解耦合
-
可拓展:搭建平台def object_play(herofight1: HeroFight1, enemyfight),在不做任何修改的情况下,可以调用后来人写的代码
4、拓展:在Python中还有哪些多态的案例呢?
+ 多态体现
+加号只有一个,但是不同的对象调用+方法,其返回结果不同。
如果加号的两边都是数值类型的数据,则加号代表运算符
如果加号的两边传入的是字符串类型的数据,则加号代表合并操作,返回合并后的字符串
‘a’ + ‘b’ = ‘ab’
如果加号的两边出入序列类型的数据,则加号代表合并操作,返回合并后的序列
[1, 2, 3] + [4, 5, 6] = [1, 2, 3, 4, 5, 6]
三、面向对象其他特性
1、类属性
Python中,属性可以分为实例属性和类属性。
类属性就是 类对象中定义的属性,它被该类的所有实例对象所共有。通常用来记录 与这类相关 的特征,类属性 不会用于记录 具体对象的特征。
在Python中,一切皆对象。类也是一个特殊的对象,我们可以单独为类定义属性。
示例:
class Person(object):
# 定义类属性
count = 0
def __init__(self, name, age):
self.name = name
self.age = age
2、类属性代码实现
定义count类属性,用于记录实例化Person类,产生对象的数量。
示例:
class Person(object):
# 定义类属性count
count = 0
# 定义一个__init__魔术方法,用于进行初始化操作
def __init__(self, name):
self.name = name
# 对count类属性进行+1操作,用于记录一共生成了多少个对象
Person.count += 1
# 实例化对象
p1 = Person('小明')
p2 = Person('小华')
p3 = Person('小红')
# 输出类属性
print(f'一个生成了{Person.count}个实例化对象')
'''
一个生成了3个实例化对象
'''
3、类方法
所谓类方法,指的是类所拥有的方法,并需要使用[装饰器]@classmethod来标识其为类方法
同时一定要注意的是对于类方法的第一个参数必须是类对象,通常以cls作为第一个参数名,格式如下:
@classmethod
def 类方法名(cls):
...
调用时,格式如下:
# 方式一:
类名.类方法名() # 推荐使用
# 方式二:
对象名.类方法名()
示例:
# 1.定义类
class Dog(object):
__num = 6
# 类方法
@classmethod
def eat(cls):
print(cls.__num)
print('小狗喜欢吃骨头')
# 2.调用类方法
# 方式一:
Dog.eat()
# 方式二:
dog = Dog()
dog.eat()
'''
6
小狗喜欢吃骨头
6
小狗喜欢吃骨头
'''
类方法主要用于操作类属性或类中的其他方法。
4、静态方法
静态方法需要通过装饰器@staticmethod来来标识其为静态方法,且静态方法不需要多定义参数,格式如下:
@staticmethod
def 静态方法名():
...
调用时,格式如下:
# 方式一:
类名.静态方法名 # 推荐使用
# 方式二:
对象名.静态方法名
示例:
# 开发一款游戏
class Game(object):
# 开始游戏,打印游戏功能菜单
@staticmethod
def menu():
print('1、开始游戏')
print('2、游戏暂停')
print('3、退出游戏')
# 开始游戏、打印菜单
Game.menu()
'''
1、开始游戏
2、游戏暂停
3、退出游戏
'''