面向对象
- 1.初识对象
- 1.1 生活中数据的组织
- 1. 2 程序中数据的组织
- 1.3 使用对象组织数据
- 1.4进行对比
- 2. 成员方法
- 2.1 类的定义和使用
- 2.2 成员变量和成员方法
- 2.2.1 成员变量
- 2.2.2 成员方法的定义语法
- 2.2.3 self 的作用
- 3. 类和对象
- 3.1 面向过程
- 3.2 面向对象
- 4.构造方法
- 4.1 属性成员变量的赋值
- 5. 其他内置方法
- 6. 封装
- 6.1 **面向对象的三大特性**
- 6.2 封装
- 6.3 私有成员
- 6.4 使用私有成员
- 7.继承
- 7.1 单继承
- 7.2 多继承
- 7.3 复写父类成员
- 7.4 调用父类同名成员
- 8. 类型注解
- 8.1 变量类型注解
- 语法一: **变量名 : 类型**
- 语法二: type: 类型
- 类型注解的限制
- 8.2 函数和方法的形参类型注解:
- 8.3 Union类型
- 9. 多态
- 9.1 多态
- 9.2 抽象类(接口)
- 9.3 抽象类的作用
1.初识对象
1.1 生活中数据的组织
比如,我们要统计学生的信息
1. 2 程序中数据的组织
student_1 = {
"学号" : "111",
"姓名" : "张三",
"性别" : "男",
"年龄" : "19"
}
student_2 = {
"学号" : "222",
"姓名" : "李四",
"性别" : "男",
"年龄" : "18"
}
#......
# 每次都要重复创建字典
思考:
没次的操作都是重复的
如果程序中也和生活中一样
• 可以设计表格
• 可以将设计的表格打印出来
• 可以将打印好的表格供人填写内容
那么数据的组织就非常方便了。
1.3 使用对象组织数据
在程序中是可以做到和生活中那样,设计表格、生产表格、填写表格的组织形式的。
- 在程序中设计表格,我们称之为:设计类( class )
class Student:
#这些定义的变量成为成员变量
#记录年龄
id = None
#记录性别
name = None
#记录性别
sex = None
#记录年龄
age = None
- 在程序中打印生产表格,我们称之为:创建对象
#使用上述创建的类来创建对象
student_1 = Student()
student_2 = Student()
#数据怎么填入呢?
- 在程序中填写表格,我们称之为:对象属性赋值
# 对student_1 赋值
student_1.id = "111"
student_1.name = "张三"
student_1.sex = "男"
student_1.age = 19
# 对student_2 赋值
student_2.id = "222"
student_2.name = "李四"
student_2.sex = "男"
student_2.age = 18
1.4进行对比
在程序中:
设计表格,称之为:设计类(class)
打印表格,称之为:创建对象
填写表格,称之为:对象属性赋值
2. 成员方法
2.1 类的定义和使用
在上一节中,我们简单了解到可以使用类去封装属性,并基于类创建出一个个的对象来使用。
现在我们来看看类的使用语法:
2.2 成员变量和成员方法
2.2.1 成员变量
class Student:
#成员变量 年龄
id = None
#成员变量 性别
name = None
#成员变量 别
sex = None
#成员变量 年龄
age = None
#成员方法 self必填
def Print(self):
print(f"学号{self.id}, 姓名{self.name}, 性别{self.sex}, 年龄{self.age}")
可以看出,类中:
不仅可以定义属性用来记录数据也可以定义函数,用来记录行为
其中:
类中定义的属性(变量),我们称之为:成员变量
类中定义的行为(函数),我们称之为:成员方法
2.2.2 成员方法的定义语法
在类中定义成员方法和定义函数基本一致,但仍有细微区别:
#只是多加一个self, 其余参数和往常函数一样
def Fun(self, 形参1, ....,形参N):
#方法体
可以看到,在方法定义的参数列表中,有一个:self关键字
self关键字是成员方法定义的时候,必须填写的。它用来表示类对象自身的意思。当我们使用类对象调用方法的是,self会自动被python传入在方法内部,想要访问类的成员变量,必须使用self
2.2.3 self 的作用
注意:
- self关键字,尽管在参数列表中,但是传参的时候可以忽略它。
- 表示类对象本身的意思
- 只有通过self,成员方法才能访问类的成员变量
- self出现在形参列表中,但是不占用参数位置,无需理会
class Student:
#成员变量 年龄
id = None
#成员变量 姓名
name = None
#成员变量 性别
sex = None
#成员变量 年龄
age = None
#成员方法
def Print(self):
print(f"学号{self.id}, 姓名{self.name}, 性别{self.sex}, 年龄{self.age}")
#创建一个对象
student_1 = Student()
# 对student_1 赋值
student_1.id = "111"
student_1.name = "张三"
student_1.sex = "男"
student_1.age = 19
#不需要传参 self
student_1.Print()
3. 类和对象
3.1 面向过程
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
拿做饭举例:
3.2 面向对象
Python是面向对象的,关注的是对象将一件事情拆分成不同的对象,靠对象之间的交互完成。
上述过程一共有四个对象:人,菜, 锅,火
整个主要过程是:人,菜, 锅,火 四个对象交互完成的,人不需要关注机器人是怎么洗菜,开火的
4.构造方法
4.1 属性成员变量的赋值
#创建一个对象
student_1 = Student()
# 对student_1 赋值
student_1.id = "111"
student_1.name = "张三"
student_1.sex = "男"
student_1.age = 19
上面代码中,为对象的属性赋值需要依次进行,略显繁琐。
有没有更加高效的方式,能够一行代码就完成呢?
答:当然可以
- Python类可以使用:init()方法,称之为构造方法。
- init 两旁是 两个下划线 " _ "
- 构造方法也是成员方法,不要忘记在参数列表中提供:self
可以实现:
- 在创建类对象(构造类)的时候,会自动执行。
- 在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用。
class Student:
# 成员变量可以省略不写
#成员变量 年龄
id = None
#成员变量 姓名
name = None
#成员变量 性别
sex = None
#成员变量 年龄
age = None
# 构造方法
def __init__(self,id , name, sex, age):
self.id = id
self.name = name
self.sex = sex
self.age = age
#成员方法
def Print(self):
print(f"学号{self.id}, 姓名{self.name}, 性别{self.sex}, 年龄{self.age}")
#创建对象,同时赋值
student_1 = Student("111", "张三", "男", 19)
student_2 = Student("222", "李四", "男", 18)
- 在构造方法内定义成员变量,需要使用self关键字
# 构造方法
def __init__(self,id , name, sex, age):
self.id = id #成员变量 年龄
self.name = name #成员变量 姓名
self.sex = sex #成员变量 性别
self.age = age #成员变量 年龄
这是因为:变量是定义在构造方法内部,如果要成为成员变量,需要用self来表示。
5. 其他内置方法
上文学习的__init__ 构造方法,是Python类内置的方法之一。
这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为:魔术方法
class Student:
# 构造方法
def __init__(self,id , name, sex, age):
self.id = id #成员变量 年龄
self.name = name #成员变量 姓名
self.sex = sex #成员变量 性别
self.age = age #成员变量 年龄
# 返回f"学号{self.id}, 姓名{self.name}, 性别{self.sex}, 年龄{self.age}字符串
def __str__(self):
return f"学号{self.id}, 姓名{self.name}, 性别{self.sex}, 年龄{self.age}"
# 年龄大于、小于比较
def __lt__(self,other):
return self.age < other.age
# 年龄大于等于、小于等于比较
def __le__(self,other):
return self.age <= other.age
# 年龄等于比较
def __eq__(self,other):
return self.age == other.age
#创建对象,同时赋值
student_1 = Student("111", "张三", "男", 19)
student_2 = Student("222", "李四", "男", 18)
# 自动调用__str__(self)
#不实现__str__ 打印的是地址
print(student_1)
print(student_2)
# 自动调用__lt__(self,other)
#不实现__lt__ 比较的是地址
print(student_1 < student_2)
# 自动调用__le__(self,other)
#不实现__le__ 比较的是地址
print(student_1 >= student_2)
# 自动调用__eq__(self,other)
#不实现__eq__ 比较的是地址
print(student_1 == student_2)
6. 封装
6.1 面向对象的三大特性
面向对象编程,是许多编程语言都支持的一种编程思想。
简单理解是:基于模板(类)去创建实体(对象),使用对象完成功能开发。
面向对象包含3大主要特性:
- 封装
- 继承
- 多态
6.2 封装
封装表示的是,将现实世界事物的:
属性
行为
封装到类中,描述为:
成员变量
成员方法
从而完成程序对现实世界事物的描述
6.3 私有成员
- 什么是私有成员?
- 答:现实事物有部分属性和行为是不公开对使用者开放的。同样在类中描述属性和方法的时候也需要达到这个要求,就需要定义私有成员了。私有成员只能在成员方法内使用。
类中提供了私有成员的形式来支持:
1.私有成员变量
2.私有成员方法
定义私有成员的方式非常简单,只需要:
1.私有成员变量:变量名以__开头(2个下划线)
2.私有成员方法:方法名以__开头(2个下划线)
3.即可完成私有成员的设置
class Student:
#私有成员变量
__id = None
__name = None
__sex = None
__age = None
# 构造方法
def __init__(self, id, name, sex, age):
self.id = id # 成员变量 年龄
self.name = name # 成员变量 姓名
self.sex = sex # 成员变量 性别
self.age = age # 成员变量 年龄
# 私有成员方法
def __Print(self):
print(f"学号{self.id}, 姓名{self.name}, 性别{self.sex}, 年龄{self.age}")
6.4 使用私有成员
错误用法:
class Student:
#私有成员变量
__id = None
__name = None
__sex = None
__age = None
#这样的构造方法是错误的
# 私有变量无法赋值,也无法获取值,会报错
def __init__(self, id, name, sex, age):
self.id = id # 成员变量 年龄
self.name = name # 成员变量 姓名
self.sex = sex # 成员变量 性别
self.age = age # 成员变量 年龄
# 私有成员方法
def __Print(self):
print(f"学号{self.id}, 姓名{self.name}, 性别{self.sex}, 年龄{self.age}")
#创建对象,同时赋值
#私有变量无法赋值,也无法获取值,会报错
student= Student("111", "张三", "男", 19)
#私有方法无法直接被类对象使用
student.Print()
正确用法:
class Test:
n =None
__m = None
def __init__(self,n):
self.n = n
def __Print(self):
print("私有成员")
def Fun(self):
# 在成员方法内可以调用私有成员变量
self.n = 0
# 在成员方法内可以调用私有成员方法
print(self.n)
self.__Print()
test = Test(9)
test.Fun()
7.继承
继承就是一个类,继承另外一个类的成员变量和成员方法
继承表示:将从父类那里继承(复制)来成员变量和成员方法(不含私有)
子类构建的类对象,可以
1.有自己的成员变量和成员方法
2.使用父类的成员变量和成员方法
比如,我们要更新一款软件,更新之后的软件会失去之前的功能吗? 当然不会。更新之后的软件会继承之前的某些功能。
语法:
#在括号内填写 父类名
class father(父类1, 父类2,...):
#类内容体
7.1 单继承
简单的说就是继承单个类(一个父类)
```python
# 父类
class Father:
a = None
def Fun_1(self):
print("父类")
self.a = 1
print(self.a)
#子类
class Brother(Father):
b = None
def Fun_2(self):
print("子类")
self.b = 2
#使用父类的成员变量
print(Father.a)
#使用父类的成员方法 要传参self
Father.Fun_1(self)
test = Brother()
#调用自己的成员方法
test.Fun_2()
#调用父类的成员方法
test.Fun_1()
7.2 多继承
Python的类之间也支持多继承,即一个类可以继承多个父类
class Father_1:
def Fun_1(self):
print("父类一")
class Father_2:
def Fun_2(self):
print("父类二")
class Brother(Father_1, Father_2):
def Fun_3(self):
print("子类")
test = Brother()
# 调用父类一
test.Fun_1()
# 调用父类二
test.Fun_2()
# 调用子类
test.Fun_3()
多个父类中,如果有同名的成员怎么办呢?
答:多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。
即:先继承的保留,后继承的被覆盖
class Father_1:
def Fun(self):
print("父类一")
class Father_2:
def Fun(self):
print("父类二")
class Brother(Father_1, Father_2):
def Fun_1(self):
print("子类")
test = Brother()
# 调用父类的成员 ,调用的是父类一
test.Fun()
7.3 复写父类成员
子类继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写。
即:在子类中重新定义同名的属性或方法即可。
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
class Father_1:
def Fun(self):
print("父类一")
class Father_2:
def Fun(self):
print("父类二")
class Brother(Father_1, Father_2):
def Fun(self):
print("子类")
test = Brother()
# 调用和父类的一样成员方法 ,调用的是自己的
test.Fun()
7.4 调用父类同名成员
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
如果需要使用被复写的父类的成员,需要特殊的调用方式:
-
方式1:
调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self) -
方式2:
使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()
只能在子类内调用父类的同名成员。
子类的类对象直接调用 会调用子类复写的成员
class Father:
n = 1
def Fun(self):
print("父类")
class Brother(Father):
def Fun(self):
print("子类")
print()
#方式一调父类成员
print(Father.n)
Father.Fun(self)
print()
#方法二调用父类成员
print(super().n)
super().Fun()
test = Brother()
test.Fun()
8. 类型注解
Python在3.5版本的时候引入了类型注解,以方便静态类型检查工具,IDE等第三方工具。
类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)。
主要功能:
帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示,帮助开发者自身对变量进行类型注释
支持:
变量的类型注解
函数(方法)形参列表和返回值的类型注解
8.1 变量类型注解
#变量类型注解
a :int = 1
b :float = 1.0
c :str = "1234"
d :bool = True
#类对象类型注解
class Test:
pass
test :Test = Test()
#变量类型注解
a = 1 # type: int
b = 1.0 # type: float
c = "1234" # type: str
d = True # type: bool
#类对象类型注解
class Test:
pass
test = Test() # type: Test
一般,无法直接看出变量类型之时会添加变量的类型注解
类型注解主要功能在于:
帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示
帮助开发者自身对变量进行类型注释(备注)
并不会真正的对类型做验证和判断。
也就是,类型注解仅仅是提示性的,不是决定性的
如上图,是不会报错的哦。
8.2 函数和方法的形参类型注解:
如图所示:
在调用函数(方法),将输入参数的时候,工具没有任何提示(下图)
在定义函数(方法)的时候,给形参进行注解(下图)
函数和方法的形参类型注解语法:
def 函数(方法)名(形参 :类型, 形参 :类型,........) -> 返回值类型 :
#...
样例:
8.3 Union类型
上面的注释是单个的,给大家看一个列表的注释:
my_list :list[int] = [1, 2, 3, 4]
my_dict :dict[str,int] = {"age" :19, "id" : 1234}
如果我们的 my_list 里面还有其他类型就还需要在相应位置加入相应的类型,这样的话是不是就变得复杂了?有没有解决方法呢?
答:有,Union类型
Union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用。
使用Union[类型, …, 类型] 可以定义联合类型注解
#导包
from typing import Union
# 多个类型 多个类型
my_list :list[ Union[int, float] ]= [1, 2, 3, 4.0, 5.0]
# str 配多个类型
my_dict :dict[str,Union[int, str]] = {"age" :19, "id" : 1234, "name" : "张三"}
9. 多态
9.1 多态
多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。
如何理解?
答:比如,老师布置一个任务,同学们用不同的方法解决了这个任务
class homework:
def English(self):
pass
#同学接收到作业后开始写
class student_1(homework):
def English(self):
print("student_1 英语的方法")
#同学接收到作业后开始写
class student_2(homework):
def English(self):
print("student_2 英语的方法")
#同样的行为(函数),传入不同的对象,得到不同的状态
#展示出学生的作业
def Fun(student : homework):
student.English()
#创建对象
stu_1 = student_1()
stu_2 = student_2()
#调用函数
Fun(stu_1)
Fun(stu_2)
9.2 抽象类(接口)
细心的同学可能发现了,父类 homework 的 English 方法,是空实现
这种设计的含义是:
父类用来确定有哪些方法
具体的方法实现,由子类自行决定
这种写法,就叫做抽象类(也可以称之为接口)
抽象类:含有抽象方法的类称之为抽象类
抽象方法:方法体是空实现的(pass)称之为抽象方法
9.3 抽象类的作用
多用于做顶层设计(设计标准),以便子类做具体实现。
对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法
并配合多态使用,获得不同的工作状态
- 抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现。(下列代码)
class homework:
#语文
def Chinses(self):
pass
#数学
def Math(self):
pass
#英语
def English(self):
pass
class student_1(homework):
def Chinese(self):
print("student_1 语文的方法")
def Math(self):
print("student_1 数学的方法")
def English(self):
print("student_1 英语的方法")
class student_2(homework):
def Chinese(self):
print("student_2 语文的方法")
def Math(self):
print("student_2 数学的方法")
def English(self):
print("student_2 英语的方法")
- 多态,完成抽象的父类设计(设计标准)和 具体的子类实现(实现标准),下列代码
#语文
def Fun_Chinese(student : homework):
student.Chinese()
#数学
def Fun_Math(student : homework):
student.Math()
#英语
def Fun_English(student : homework):
student.English()
#创建对象
stu_1 = student_1()
stu_2 = student_2()
#每位同学的语文
Fun_Chinese(stu_1)
Fun_Chinese(stu_2)
#每位同学的数学
Fun_Math(stu_1)
Fun_Math(stu_2)
#每位同学的英语
Fun_English(stu_1)
Fun_English(stu_2)