cf网站编程,锡盟网站建设,wordpress mx主题VIP,百度云虚拟主机上传wordpress第十三章#xff0c;面向对象
初识对象
生活中数据的组织
学校开学#xff0c;要求学生填写自己的基础信息#xff0c;一人发一张白纸#xff0c;让学生自己填
我叫林军杰#xff0c;今年31岁.来自山东省#xff0c;我是男的#xff0c;中国人
内容混乱
改为登记表…第十三章面向对象
初识对象
生活中数据的组织
学校开学要求学生填写自己的基础信息一人发一张白纸让学生自己填
我叫林军杰今年31岁.来自山东省我是男的中国人
内容混乱
改为登记表打印出来让学生自行填写
姓名 林军杰
姓别 男
国籍 中国
籍贯 山东省
年龄 31
整洁明了
程序中数据的组织
在程序中简单使用变量来记录学生信息
student_1 { 姓名周杰轮 性别男 国籍中国 籍贯台湾省 年龄33
}
student_2 我叫林军杰今年31岁男的来自中国山东省
student_3 [ 邓紫旗 中国 北京市 26 女
]
混乱、不统一
思考
使用变量记录数据太乱了。
如果程序中也和生活中一样
可以设计表格可以将设计的表格打印出来可以将打印好的表格供人填写内容
那么数据的组织就非常方便了。
使用对象组织数据
在程序中是可以做到和生活中那样设计表格、生产表格、填写表格的组织形式的。
1.在程序中设计表格我们称之为设计类(class)
class Student: name None # 记录学生姓名
2.在程序中打印生产表格我们称之为创建对象
# 基于类创建对象
stu_1 Student()
stu_2 Student()
3.在程序中填写表格我们称之为对象属性赋值
stu_1.name 周杰轮 # 为学生1对象赋予名称属性值
stu_2.name 林军杰 # 为学生2对象赋予名称属性值
例
# 1. 设计一个类类比生活中设计一张登记表
class Student: name None # 记录学生姓名 gender None # 记录学生性别 nationality None # 记录学生国籍 native_place None # 记录学生籍贯 age None # 记录学生年龄
# 2. 创建一个对象类比生活中打印一张登记表
stu_1 Student()
# 3. 对象进行赋值类比生活中填写表单
stu_1.name 林俊杰
stu_1.gender 男
stu_1.nationality 中国
stu_1.native_place 山东省
stu_1.age 31
# 4. 获取对象中记录的信息
print(stu_1.name)
print(stu_1.gender)
print(stu_1.nationality)
print(stu_1.native_place)
print(stu_1.age)
总结
1.生活中或是程序中我们都可以使用设计表格、生产表格、填写表格的形式组织数据
2.进行对比在程序中
设计表格称之为设计类class打印表格称之为创建对象填写表格称之为对象属性赋值
成员方法
类的定义和使用
在上一节中我们简单了解到可以使用类去封装属性并基于类创建出一个个的对象来使用。
现在我们来看看类的使用语法
class 类名称 class是关键字表示要定义类了 类的属性 类的属性即定义在类中的变量成员变量 类的行为 类的行为即定义在类中的函数成员方法
创建类对象的语法
对象 类名称()
类(class
属性(数据)行为(函数)
成员变量和成员方法
那么什么是类的行为(方法)呢
class Student: name None age None def say_hi(self): print(fHello 大家好我是{self.name})
stu Student()
stu.name 周杰伦
stu.say_hi()
可以看出类中
不仅可以定义属性用来记录数据也可以定义函数用来记录行为
其中
类中定义的属性(变量)我们称之为成员变量类中定义的行为(函数)我们称之为成员方法
从今天开始定义在类内部的函数称之为方法
成员方法的定义语法
在类中定义成员方法和定义函数基本一致但仍有细微区别
def 方法名self形参1......形参N) 方法体
可以看到在方法定义的参数列表中有一个self关键字
self关键字是成员方法定义的时候必须填写的。
它用来表示类对象自身的意思当我们使用类对象调用方法的是self会自动被python传入在方法内部想要访问类的成员变量必须使用self
注意事项
self关键字尽管在参数列表中但是传参的时候可以忽略它。
如
class Student: name None def say_hi(self): print(fHello 大家好我是{self.name}) def say_hi2(self, msg): print(fHello 大家好我是{self.name}{msg})
stu Student()
stu.name 周杰伦
stu.say_hi() # 调用的时候无需传参
stu.say_hi2(很高兴认识大家) # 调用的时候需要传msg参数
可以看到在传入参数的时候self是透明的可以不用理会它。
总结
1.类是由哪两部分组成呢
类的属性称之为成员变量类的行为称之为成员方法
注意函数是写在类外的定义在类内部我们都称之为方法哦
2.类和成员方法的定义语法
class 类名称 成员变量 def 成员方法(self参数列表) 成员方法体
对象 类名称()
3.self的作用
表示类对象本身的意思只有通过self成员方法才能访问类的成员变量self出现在形参列表中但是不占用参数位置无需理会
类和对象
现实世界的事物和类
现实事物
属性行为
现实世界的事物也有属性和行为类也有属性和行为。
使用程序中的类可以完美的描述现实世界的事物
类和对象
基于类创建对象的语法对象名 类名称()
为什么非要创建对象才能使用呢
类只是一种程序内的“设计图纸”需要基于图纸生产实体(对象)才能正常工作
这种套路称之为面向对象编程
基于类创建对象
# 设计一个闹钟类
class Clock: id None # 序列号 price None # 价格 def ring(self): import winsound winsound.Beep(2000, 1000)
# 构建两个闹钟对象并让其工作
clock1 Clock()
clock1.id 003032
clock1.price 19.99
print(f闹钟ID{clock1.id}价格{clock1.price})
clock1.ring()
clock2 Clock()
clock2.id 003033
clock2.price 21.99
print(f闹钟ID{clock2.id}价格{clock2.price})
clock2.ring()
这就是面向对象编程
设计类基于类创建对象由对象做具体的工作
总结
1.现实世界的事物由什么组成?
属性行为
类也可以包含属性和行为所以使用类描述现实世界事物是非常合适的
2.类和对象的关系是什么?
类是程序中的“设计图纸”
对象是基于图纸生产的具体实体
3.什么是面向对象编程?
面向对象编程就是使用对象进行编程。
即设计类基于类创建对象并使用对象来完成具体的工作
构造方法
属性(成员变量)的赋值
class Student: name None # 名称 age None # 年龄 tel None # 手机号
student1 Student()
student1.name 周杰轮
student1.age 31
student1.tel 18012340000
student2 Student()
student2.name 周杰轮
student2.age 31
student2.tel 18012340000
上述代码中为对象的属性赋值需要依次进行略显繁琐。有没有更加高效的方式能够一行代码就完成呢
思考Student()
这个括号能否像函数方法那样通过传参的形式对属性赋值呢
可以需要使用构造方法__init__()
构造方法
Python类可以使用__init__()方法称之为构造方法。
可以实现
在创建类对象构造类的时候会自动执行。在创建类对象构造类的时候将传入参数自动传递给init方法使用。
# 构造方法的名称__init__
class Student: # name None # age None # tel None def __init__(self, name, age, tel): self.name name self.age age self.tel tel print(Student类创建了一个类对象)
stu Student(周杰伦, 31, 18500006666)
print(stu.name)
print(stu.age)
print(stu.tel)
构建类时传入的参数会自动提供给__init__方法构建类的时候__init__方法会自动执行
构造方法注意事项
重要的事情说三遍构造方法名称__init__ __init__ __init__,千万不要忘记init前后都有2个下划线
构造方法也是成员方法不要忘记在参数列表中提供self
在构造方法内定义成员变量需要使用self关键字
def __init__(self, name, age, tel): self.name name # 名称 self.age age # 年龄 self.tel tel # 手机号
这是因为变量是定义在构造方法内部如果要成为成员变量、需要用self来表示。
总结
1.构造方法的名称是
init注意init前后的2个下划线符号
2.构造方法的作用
构建类对象的时候会自动运行构建类对象的传参会传递给构造方法借此特性可以给成员变量赋值
3.注意事项
构造方法不要忘记self关键字在方法内使用成员变量需要使用self
练习
学生信息录入
开学了有一批学生信息需要录入系统请设计一个类记录学生的姓名、年龄、地址这3类信息
请实现
通过for循环配合input输入语句并使用构造方法完成学生信息的键盘录入输入完成后使用print语句完成信息的输出
输出示例
当前录入第1位学生信息总共需录入10位学生信息
请输入学生姓名周杰轮
请输入学生年龄31
请输入学生地址北京
学生1信息录入完成信息为【学生姓名周杰轮年龄31地址北京】当前录入第2位学生信息总共需录入10位学生信息
请输入学生姓名
参考代码
class Student: def __init__(self, name, age, address): self.name name self.age age self.address address
for x in range(1, 11): print(f当前录入第{x}名学生信息总共需录入10名学生信息) name input(请输入学生姓名) age input(请输入学生年龄) address input(请输入学生地址) student Student(name, age, address) print(f学生{x}信息录入完成信息为【学生姓名{student.name}年龄{student.age}地址{student.address}】)
其它内置方法
魔术方法
上文学习的 init 构造方法是Python类内置的方法之一。
这些内置的类方法各自有各自特殊的功能这些内置方法我们称之为魔术方法
魔术方法
__init__构造方法__str__ 字符串方法__It__小于、大于符号比较__le__ 小于等于、大于等于符号比较__eq__ 符号比较
魔术方法非常多学习几个常见的即可
__str__字符串方法
class Student: def __init__(self, name, age): self.name name # 学生姓名 self.age age # 学生年龄
stu Student(周杰伦, 31)
print(stu) main.Student object at 0x0000021606059760
print(str(stu)) main.Student object at 0x0000021606059760
当类对象需要被转换为字符串之时会输出如上结果(内存地址)
内存地址没有多大作用我们可以通过__str__方法控制类转换为字符串的行为。
class Student: def __init__(self, name, age): self.name name # 学生姓名 self.age age # 学生年龄 def __str__(self): return fStudent类对象name:{self.name}, age:{self.age}
stu Student(周杰伦, 31)
print(stu) # 结果Student类对象name:周杰伦, age:31
print(str(stu)) # 结果Student类对象name:周杰伦, age:31
方法名__str__返回值字符串内容自行定义
__lt__小于符号比较方法
class Student: def __init__(self, name, age): self.name name # 学生姓名 self.age age # 学生年龄
stu1 Student(周杰伦, 31)
stu2 Student(林俊杰, 36)
print(stu1 stu2)
# 结果报错 TypeError: not supported between instances of Student and Student
直接对2个对象进行比较是不可以的但是在类中实现__lt__方法即可同时完成小于符号 和 大于符号 2种比较
class Student: def __init__(self, name, age): self.name name # 学生姓名 self.age age # 学生年龄 def __lt__(self, other): return self.age other.age
stu1 Student(周杰伦, 31)
stu2 Student(林俊杰, 36)
print(stu1 stu2) # 结果True
print(stu1 stu2) # 结果Falue
方法名__lt__传入参数other另一个类对象返回值True 或 False内容自行定义
__le__小于等于比较符号方法
魔术方法le可用于两种比较运算符上。
class Student: def __init__(self, name, age): self.name name # 学生姓名 self.age age # 学生年龄 def __le__(self, other): return self.age other.age
stu1 Student(周杰伦, 31)
stu2 Student(林俊杰, 36)
print(stu1 stu2) # 结果True
print(stu1 stu2) # 结果Falue
方法名__le__传入参数other另一个类对象返回值True 或 False内容自行定义
__eq__比较运算符实现方法
class Student: def __init__(self, name, age): self.name name # 学生姓名 self.age age # 学生年龄 def __eq__(self, other): return self.age other.age
stu1 Student(周杰伦, 36)
stu2 Student(林俊杰, 36)
print(stu1 stu2) # 结果Ture
方法名__eq__传入参数other另一个类对象返回值True 或 False内容自行定义
不实现_eq 方法对象之间可以比较但是是比较内存地址也即是不同对象比较一定是False结果。
实现了eq 方法就可以按照自己的想法来决定2个对象是否相等了。
总结
方法 功能
__init__ 构造方法可用于创建类对象的时候设置初始化行为
__sr__ 用于实现类对象转字符串的行为
__It__ 用于2个类对象进行小于或大于比较
__le__ 用于2个类对象进行小于等于或大于等于比较
__eq__ 用于2个类对象进行相等比较
封装
面向对象的三大特性
面向对象编程是许多编程语言都支持的一种编程思想。
简单理解是基于模板(类)去创建实体(对象)使用对象完成功能开发。
面向对象包含3大主要特性
封装继承多态
封装
封装表示的是将现实世界事物的
属性行为
封装到类中描述为
成员变量成员方法
从而完成程序对现实世界事物的描述
对用户隐藏的属性和行为
现实世界中的事物有属性和行为。
但是不代表这些属性和行为都是开放给用户使用的。
以手机为例
对用户开放的属性和行为
序列号品牌型号长宽高上网通话拍照
对用户隐藏的属性和行为
运行电压驱动信息程序调度内存管理
苹果越狱、安卓root也是为了突破权限使用这些对用户隐藏的属性和行为
私有成员
既然现实事物有不公开的属性和行为那么作为现实事物在程序中映射的类也应该支持。
类中提供了私有成员的形式来支持。
私有成员变量私有成员方法
定义私有成员的方式非常简单只需要
私有成员变量变量名以__开头(2个下划线)
私有成员方法方法名以__开头(2个下划线)
即可完成私有成员的设置
class Phone: __current_voltage None # 私有成员变量 def __keep_single_core(self): print(让CPU以单核模式运行) # 私有成员方法
使用私有成员
私有方法无法直接被类对象使用
私有变量无法赋值也无法获取值
class Phone: __current_voltage None # 当前手机运行电压 def __keep_single_core(self): print(让CPU以单核模式运行)
phone Phone()
phone.__keep_single_core
print(phone.__current_voltage)
# 报错 AttributeError: Phone object has no attribute __keep_single_core. Did you mean: _Phone__keep_single_core?
私有成员无法被类对象使用但是可以被其它的成员使用。
class Phone: __current_voltage 0.5 # 当前手机运行电压 def __keep_single_core(self): print(让CPU以单核模式运行) def call_by_5g(self): if self.__current_voltage 1: print(5G通话开启) else: self.__keep_single_core() print(电量不足无法使用5g通话并已设置为单核运行进行省电)
phone Phone()
phone.call_by_5g()
总结
1.封装的概念是指?
将现实世界事物在类中描述为属性和方法即为封装。
2.什么是私有成员?为什么需要私有成员?
现实事物有部分属性和行为是不公开对使用者开放的。同样在类中描述属性和方法的时候也需要达到这个要求就需要定义私有成员了
3.如何定义私有成员?
成员变量和成员方法的命名均以_作为开头即可
4.私有成员的访问限制?
类对象无法访问私有成员类中的其它成员可以迈问私有成员
思考
私有成员的定义我们已经了解了但是
它有什关网佛聚际的意义吗
在类中提供仅供内部使用的属性和方法而不对外开放(类对象无法使用)
练习
设计带有私有成员的手机
设计一个手机类内部包含
私有成员变量__is_5g_enable类型boolTrue表示开启5gFalse表示关闭5g
私有成员方法__check_5g()会判断私有成员__is_5g_enable的值
若为True打印输出5g开启若为False打印输出5g关闭使用4g网络
公开成员方法call_by_5g()调用它会执行
调用私有成员方法check 5g()判断5g网络状态打印输出正在通话中
运行结果5g关闭使用4g网络 正在通话中
通过完成这个类的设计和使用体会封装中私有成员的作用
对用户公开的call_by_5g()方法对用户隐藏的__is_5g_enable私有变量和__check_5g私有成员
参考代码
class Phone: __is_5g_enable False def __check_5g(self): if self.__is_5g_enable: print(5g开启) else: print(5g关闭使用4g网络) def call_by_5g(self): self.__check_5g() print(正在通话中)
phone Phone()
phone.call_by_5g()
继承
继承的基础语法
继承的引出
iPhone 6 → iPhone 6s → iPhone 7 → iPhone 8
如果你是设计师你会如何选择?
1.每一代新款手机都从零开始出设计图2
2. 基于老款的设计图修修改改
class Phone: IMEI None # 序列号 producer HM # 厂商 def call_by_4g(self): print(4g通话)
构建Phone2022类你会选择
1. 从头写一个新的类
2.基于已有的Phone类进行修改
class Phone: IMEI None # 序列号 producer HM # 厂商 def call_by_4g(self): print(4g通话) def call_by_5g(self): print(2022年最新5g通话)
我们可以使用继承来完成此需求
单继承
class Phone: IMEI None # 序列号 producer HM # 厂商 def call_by_4g(self): print(4g通话)
class Phone2022(Phone): face_id 10001 # 面部识别ID def call_by_5g(self): print(2022年最新5g通话)
class 类名(父类名) 类内容体
继承分为单继承和多继承
使用如图语法可以完成类的单继承。
继承表示将从父类那里继承(复制)来成员变量和成员方法(不含私有)
多继承
Python的类之间也支持多继承即一个类可以继承多个父类
class 类名(父类1父类2......父类N) 类内容体
class Phone: IMEI None # 序列号 producer HM # 厂商 def call_by_5g(self): print(5g通话)
class NFCReader: nfc_type 第五代 producer HM def read_card(self): print(NFC读卡) def write_card(self): print(NFC写卡)
class RemoteControl: rc_type 红外遥控 def control(self): print(红外遥控开启了)
class MyPhone(Phone, NFCReader, RemoteControl): pass
phone MyPhone()
phone.call_by_5g()
phone.read_card()
phone.write_card()
phone.control()
多继承注意事项
多个父类中如果有同名的成员那么默认以继承顺序(从左到右)为优先级。
即先继承的保留后继承的被覆盖
class Phone: IMEI None # 序列号 producer ITCAST # 厂商 def call_by_5g(self): print(5g通话)
class NFCReader: nfc_type 第五代 producer HM def read_card(self): print(NFC读卡) def write_card(self): print(NFC写卡)
class RemoteControl: rc_type 红外遥控 def control(self): print(红外遥控开启了)
class MyPhone(Phone, NFCReader, RemoteControl): pass
phone MyPhone()
phone.call_by_5g()
phone.read_card()
phone.write_card()
phone.control()
print(phone.producer) # 输出为ITCAST
总结
1.什么是继承
继承就是一个类继承另外一个类的成员变量和成员方法
语法
class 类父类父类2 父类N) 类内容体
子类构建的类对象可以
有自己的成员变量和成员方法
使用父类的成员变量和成员方法
2.单继承和多继承
单继承一个类继承另一个类
多继承一个类继承多个类按照顺序从左向右依次继承
多继承中如果父类有同名方法或属性先继承的优先级高于后继承
3.pass关键字的作用是什么
pass是占位语句用来保证函数(方法)或类定义的完整性表示无内容空的意思
复写和使用父类成员
复写
子类继承父类的成员属性和成员方法后如果对其“不满意”那么可以进行复写。
即在子类中重新定义同名的属性或方法即可。
class Phone: IMEI None # 序列号 producer ITCAST # 厂商 def call_by_5g(self): print(使用5g网络进行通话)
class MyPhone(Phone): producer ITHEIMA # 复写父亲的成员属性 def call_by_5g(self): print(开启CPU单核模式确保通话的时候省电) print(使用5g网络进行通话) print(关闭CPU单核模式确保性能)
phone MyPhone()
phone.call_by_5g()
print(phone.producer)
调用父类同名成员
一旦复写父类成员那么类对象调用成员的时候就会调用复写后的新成员
如果需要使用被复写的父类的成员需要特殊的调用方式
方式1
调用父类成员
使用成员变量父类名.成员变量
使用成员方法父类名.成员方法(self)
方式2
使用super()调用父类成员
使用成员变量super().成员变量
使用成员方法super().成员方法
总结
1.复写表示
对父类的成员属性或成员方法进行重新定义
2.复写的语法
在子类中重新实现同名成员方法或成员属性即可
3.在子类中如何调用父类成员
方式1
调用父类成员
使用成员变量父类名.成员变量
使用成员方法父类名.成员方法(self)
方式2
使用super()调用父类成员
使用成员变量super().成员变量
使用成员方法super().成员方法
注意只可以在子类内部调用父类的同名成员子类的实体类对象调用默认是调用子类复写的
类型注解
变量的类型注解
类型注解
Python在3.5版本的时候引入了类型注解以方便静态类型检查工具IDE等第三方工具。
类型注解在代码中涉及数据交互的地方提供数据类型的注解(显式的说明)。
主要功能
帮助第三方IDE工具(如PyCharm)对代码进行类型推断协助做代码提示帮助开发者自身对变量进行类型注释
支持
变量的类型注解函数(方法)形参列表和返回值的类型注解
类型注解的语法
为变量设置类型注解
基础语法变量类型
基础数据类型注解
var_1: int 10
var_2: str itheima
var_3: bool True
类对象类型注解
class Student: pass
stu:Student Student()
基础容器类型注解
my_list: list [1, 2, 3]
my_tuple: tuple (1, 2, 3)
my_dict: dict {itheima: 666}
容器类型详细注解
my_list: list[int] [1, 2, 3]
my_tuple: tuple[int, str, bool] (1, itheima, True)
my_dict: dict[str, int] {itheima: 666}
注意
元组类型设置类型详细注解需要将每一个元素都标记出来字典类型设置类型详细注解需要2个类型第一个是key第二个是value
除了使用变量类型这种语法做注解外也可以在注释中进行类型注解。
语法
# type类型
在注释中进行类型注解
import json
import random
var_1 random.randint(1, 10) # type: int
var_2 json.loads({name: zhangsan}) # type: dict[str, str]
def func(): return
var_3 func() # type: int
为变量设置注解显示的变量定义一般无需注解
var_1: int 10
var_2: str itheima
var_3: bool True
如上述例子就算不写注解也明确的知晓变量的类型
一般无法直接看出变量类型之时会添加变量的类型注解
import json
import random
var_1: int random.randint(1, 10)
var_2: dict json.loads({name: zhangsan})
def func(): return
var_3: int func()
类型注解的限制
类型注解主要功能在于
帮助第三方IDE工具(如PyCharm)对代码进行类型推断协助做代码提示帮助开发者自身对变量进行类型注释(备注)
并不会真正的对类型做验证和判断。
也就是类型注解仅仅是提示性的不是决定性的
var_1: int itheima
var_2: str 123
如上述例子是不会报错的哦。
总结
1.什么是类型注解有什么作用?
在代码中涉及数据交互之时对数据类型进行显式的说明可以帮助
PyCharm等开发工具对代码做类型推断协助做代码提示开发者自身做类型的备注
2.类型注解支持
变量的类型注解函数(方法)的形参和返回值的类型注解
3.变量的类型注解语法
语法1变量类型语法2在注释中# type类型
4.注意事项
类型注解只是提示性的并非决定性的。数据类型和注解类型无法对应也不会导致错误
函数(方法)的类型注解
函数(方法)的类型注解 - 形参注解
在编写函数(方法)使用形参data的时候工具没有任何提示在调用函数(方法)传入参数的时候工具无法提示参数类型
这些都是因为我们在定义函数(方法)的时候没有给形参进行注解
函数和方法的形参类型注解语法
def 函数方法名(形参名类型形参名类型......) pass
def add(x: int, y: int): return x y
def func(data: list) pass
函数(方法)的类型注解 - 返回值注解
同时函数(方法)的返回值也是可以添加类型注解的。
语法如下
def 函数方法名(形参类型......形参类型) - 返回值类型 pass
def add(x: int, y: int) - int: return x y
def func(data: list[int]) - list[int]: return data
总结
1.函数方法可以为哪里添加注解?
形参的类型注解返回值的类型注解
2.函数方法的类型注解语法?
def 函数方法名形参类型...... 形参类型) - 返回值类型 pass
注意返回值类型注解的符号使用-
Union类型
下述变量如何进行类型注解
my_list [1, 2, itheima, itcast]
my_dict {name: 周杰伦, age: 31}
使用Union[类型......类型]
可以定义联合类型注解
from typing import Union
my_list: list[Union[str, int]] [1, 2, itheima, itcast]
my_dict: dict[str, Union[str, int]] {name: 周杰伦, age: 31}
Union联合类型注解在变量注解、函数方法形参和返回值注解中均可使用。
from typing import Union
my_list: list[Union[int, str]] [1, 2, itheima, itcast]
my_dict: dict[str, Union[str, int]] {name: 周杰伦, age: 31}
def func(data: Union[int, str]) - Union[int, str]: pass
总结
1.什么是Union类型
使用Union可以定义联合类型注解
2.Union的使用方式
导包from typing import Union使用Union[类型......类型]
多态
多态
多态指的是多种状态即完成某个行为时使用不同的对象会得到不同的状态。
如何理解?
class Animal: def speak(self): pass
class Dog(Animal): def speak(self): print(汪汪汪)
class Cat(Animal): def speak(self): print(喵喵喵)
def make_noise(animal:Animal): animal.speak()
dog Dog()
cat Cat()
make_noise(dog)
make_noise(cat)
同样的行为函数 传入不同的对象 得到不同的状态
多态常作用在继承关系上.
比如
函数(方法)形参声明接收父类对象实际传入父类的子类对象进行工作
即
以父类做定义声明以子类做实际工作用以获得同一行为不同状态
抽象类(接口)
细心的同学可能发现了父类Animal的speak方法是空实现
class Animal: def speak(self): pass
class Dog(Animal): def speak(self): print(汪汪汪)
class Cat(Animal): def speak(self): print(喵喵喵)
这种设计的含义是
父类用来确定有哪些方法具体的方法实现由子类自行决定
这种写法就叫做抽象类(也可以称之为接口)
抽象类含有抽象方法的类称之为抽象类
抽象方法方法体是空实现的(pass)称之为抽象方法
为什么要使用抽象类呢?
空调制造标准
可以制冷可以制热左右摆风
提出标准后不同的厂家各自实现标准的要求。
抽象类就好比定义一个标准包含了一些抽象的方法要求子类必须实现。
class AC: def cool_wind(self): 制冷 pass def hot_wind(self): 制热 pass def swing_l_r(self): 左右摆风 pass
class Midea_AC(AC): def cool_wind(self): print(美的空调制冷) def hot_wind(self): print(美的空调制热) def swing_l_r(self): print(美的空调左右摆风)
class GREE_AC(AC): def cool_wind(self): print(格力空调制冷) def hot_wind(self): print(格力空调制热) def swing_l_r(self): print(格力空调左右摆风)
配合多态完成
抽象的父类设计设计标准具体的子类实现实现标准
def make_cool(ac: AC): ac.cool_wind()
midea_ac Midea_AC()
gree_ac GREE_AC()
make_cool(midea_ac) # 输出美的空调制冷
make_cool(gree_ac) # 输出格力空调制冷
总结
1.什么是多态
多态指的是同一个行为使用不同的对象获得不同的状态。
如定义函数(方法)通过类型注解声明需要父类对象实际传入子类对象进行工作从而获得不同的工作状态
2.什么是抽象类(接口)
包含抽象方法的类称之为抽象类。抽象方法是指没有具体实现的方法(pass)称之为抽象方法
3.抽象类的作用
多用于做顶层设计(设计标准)以便子类做具体实现。
也是对子类的一种软性约束要求子类必须复写(实现)父类的一些方法
并配合多态使用获得不同的工作状态。
综合案例
数据分析案例
某公司有2份数据文件现需要对其进行分析处理计算每日的销售额并以柱状图表的形式进行展示
数据内容
2011年1月销售数据.txt
2011年2月销售数据JSON.txt
1月份数据是普通文本使用逗号分割数据记录从前到后分别是(日期订单id销售额销售省份)2月份数据是JSON数据同样包含(日期订单id销售额销售省份)
需求分析:
读取数据 → 封装数据对象 → 计算数据对象 → pyecharts绘图
作为面向对象的程序员
我们全程将使用面向对象的思想来进行任务的开发
参考代码
data_define.py: 数据定义的类 class Record: def __init__(self, date, order_id, money, province): self.date date # 订单日期 self.order_id order_id # 订单ID self.money money # 订单金额 self.province province # 销售省份 def __str__(self): return f{self.date}, {self.order_id}, {self.money}, {self.province}
file_define.py: 和文件相关的类定义 import json
from data_define import Record
# 先定义一个抽象用来做顶层设计确定有哪些功能需要实现
class FileReader: def read_data(self) - list[Record]: 读取文件的数据读到的每一条数据都转换为Record对象将它们都封装到list内返回即可 pass
class TextFileReader(FileReader): def __init__(self, path): (self).path path # 定义成员变量记录文件的路径 # 复写实现抽象方法父类的方法 def read_data(self) - list[Record]: f open(self.path, r, encodingUTF-8) record_list: list[Record] [ ] for line in f.readlines(): line line.strip() # 消除读取到每一行数据中的\n data_list line.split(,) record Record(data_list[0], data_list[1], int(data_list[2]), data_list[3]) record_list.append(record) f.close() return record_list
class JsonFileReader(FileReader): def __init__(self, path): (self).path path # 定义成员变量记录文件的路径 def read_data(self) - list[Record]: f open(self.path, r, encodingUTF-8) record_list: list[Record] [ ] for line in f.readlines(): data_dict json.loads(line) record Record(data_dict[date], data_dict[order_id], int(data_dict[money]), data_dict[province]) record_list.append(record) f.close() return record_list
if __name__ __main__: text_file_reader TextFileReader(D:/2011年1月销售数据.txt) json_file_reader JsonFileReader(D:/2011年2月销售数据JSON.txt) list1 text_file_reader.read_data() list2 json_file_reader.read_data() for l in list1: print() for l in list2: print(l)
main.py: 实现步骤
1.设计一个类可以完成数据的封装
2.设计一个抽象类定义文件读取的相关功能并使用子类实现具体功能
3.读取文件生产数据对象
4.进行数据需求的选辑计算(计算每一天的销售额)
5通过PyEcharts进行图形绘制 from file_define import FileReader,TextFileReader,JsonFileReader
from data_define import Record
from pyecharts.charts import Bar
from pyecharts.options import *
from pyecharts.globals import ThemeType
text_file_reader TextFileReader(D:/2011年1月销售数据.txt)
json_file_reader JsonFileReader(D:/2011年2月销售数据JSON.txt)
jan_data: list[Record] text_file_reader.read_data()
feb_data: list[Record] json_file_reader.read_data() # 将两个月份的数据合并为1个list来存储
all_data: list[Record] jan_data feb_data
# 开始进行数据计算
data_dict { }
for record in all_data: if record.date in data_dict.keys(): # 当前日期已经有记录了所以和老记录做累加即可 data_dict[record.date] record.money else: data_dict[record.date] record.money
# 可视化图表开放
bar Bar(init_optsInitOpts(themeThemeType.LIGHT))
bar.add_xaxis(list(data_dict.keys())) # 添加x轴的数据
bar.add_yaxis(销售额, list(data_dict.values()), label_optsLabelOpts(is_showFalse)) # 添加y轴的数据
bar.set_global_opts( title_optsTitleOpts(title每日销售额)
)
bar.render(每日销售额柱状图.html)