面向对象
# 初识对象
# 使用对象组织数据
在程序中是可以做到和生活中那样,设计表格、生产表格、填写表格的组织形式的。
- 在程序中设计表格,我们称之为:类设计(class)
class Student:
name = None # 记录学生姓名
2
- 在程序中打印生产表格,我们称之为:创建对象
# 基于类创建对象
stu_1 = Student()
stu_2 = Student()
2
3
- 在程序中填写表格,我们称之为:对象属性赋值
stu_1.name = "周杰伦" # 为学生1对象赋予名称属性值
stu_2.name = "林俊杰" # 为学生2对象赋予名称属性值
2
# 成员方法
# 类的定义和使用
在上一节中,我们简单了解到可以使用类去封装属性,并基于类创建出一个个的对象来使用。
现在我们来看看类的使用语法:
class 类名称:
类的属性
类的方法
- class是关键字,表示要定义类了
- 类的属性,即定义在类中的变量(成员变量)
- 类的行为,即定义在类中的函数(成员方法)
创建类对象的语法:
对象 = 类名称()
# 成员变量和成员方法
那么,什么是类的行为(方法)呢?
class Student:
name = None
age = None
def say_hi(self):
print(f"Hi大家好,我是{self.name}")
2
3
4
5
6
- 类中定义的属性(变量),我们称之为:成员变量
- 类中定义的行为(函数),我们称之为:成员方法
# 成员方法的定义语法
在类中定义成员方法和定义函数基本一致,但仍有细微差别:
def 方法名(self, 形参1,...,形参N):
方法体
2
可以看到,在方法定义的参数列表中,有一个:self关键字
self关键字是成员方法定义的时候,必须填写的。
- 它用来表示类对象自身的意思
- 当我们使用类对象调用方法的时候,self会自动被python传入
- 在方法内部,想要访问类的成员变量,必须使用self
注意
self关键字,尽管在参数列表中,但是传参的时候可以忽略它。
如:
class Student:
name = None
def say_hi(self):
print("Hello 大家好")
def say_hi2(self, msg):
print(f"Hello 大家好,{msg}")
stu = Student()
stu.say_hi() # 调用的时候无序传参
stu.say_hi2("很高兴认识你们") # 调用的时候,需要传msg参数
2
3
4
5
6
7
8
9
10
11
12
# 类和对象
class Clock:
id:None
price:None
def ring(self):
import winsound
winsound.Beep(2000, 3000)
clock1 = Clock()
clock1.id = "003032"
clock1.price = 19.99
print(clock1.id, clock1.price)
clock1.ring()
clock2 = Clock()
clock2.id = "113032"
clock2.price = 79.99
print(clock2.id, clock2.price)
clock2.ring()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 构造方法
Python类可以使用:__init__()
方法,称之为构造方法。
可以实现:
- 在创建类对象(构造类)的时候,会自动执行。
- 在创建类对象(构造类)的时候,将传入参数自动递给__init__()方法使用。
class Student:
def __init__(self, name, age, tel):
self.name = name
self.age = age
self.tel = tel
print("Student类创建了一个类对象")
stu = Student("周杰伦", 31, "102104128412")
2
3
4
5
6
7
8
重要的事情说三遍,构造方法名称
__init__
__init__
__init__
,千万不要忘记init前后都有2个下划线构造方法也是成员方法,不要忘记在参数列表中提供: self
# 魔术方法
上面的__init__构造方法,是Python类内置的方法之一。
这些内置方法称之为魔术方法
方法名称 | 作用 |
---|---|
__init__ | 构造方法 |
__str__ | 字符串方法 |
__lt__ | 小于,大于符号比较 |
__le__ | 小于等于、大于等于符号比较 |
__eq__ | ==符号比较 |
# __str__字符串方法
class Student:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
stu_list = []
student = Student("周杰伦", 11, "长沙")
print(student)
# <__main__.Student object at 0x000002589602D550>
print(str(student))
# <__main__.Student object at 0x000002589602D550>
2
3
4
5
6
7
8
9
10
11
12
13
当类对象需要被转换为字符串之时,会输出如上结果(内存地址)
内存地址没有多大用,我们可以通过__str__
方法,控制类转换为字符串的行为。
class Student:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def __str__(self):
return f"Student类对象,name={self.name},age={self.age},address={self.address}"
stu_list = []
student = Student("周杰伦", 11, "长沙")
print(student)
# Student类对象,name=周杰伦,age=11,address=长沙
print(str(student))
# Student类对象,name=周杰伦,age=11,address=长沙
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# __lt__小于符号比较方法
class Student:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def __str__(self):
return f"Student类对象,name={self.name},age={self.age},address={self.address}"
stu_list = []
student = Student("周杰伦", 11, "长沙")
student2 = Student("林俊杰", 13, "武汉")
print(student < student2)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
直接对2个对象进行比较是不可以的,但是在类中实现__lt__方法,即可同时完成:小于符号和大于符号2种比较。
- 方法名:lt
- 传入参数: other, 另一个对象
- 返回值: True 或 False
- 内容: 自行定义
class Student:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def __lt__(self, other):
return self.age < other.age
stu_list = []
student = Student("周杰伦", 11, "长沙")
student2 = Student("林俊杰", 13, "武汉")
print(student > student2) # False
2
3
4
5
6
7
8
9
10
11
12
13
14
# __le__小于等于比较符号方法
魔术方法:__le__
可用于 <= 、>=两种比较运算符上。
class Student:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def __le__(self, other):
return self.age <= other.age
stu_list = []
student = Student("周杰伦", 11, "长沙")
student2 = Student("林俊杰", 13, "武汉")
print(student >= student2) # False
2
3
4
5
6
7
8
9
10
11
12
13
14
# __eq__比较运算符实现方法
不实现__eq__方法,对象之间可以比较,但是是比较内存地址,不同对象比较一定是False结果。
实现了__eq__方法,就可以按照自己的想法来决定2个对象是否相等了。
class Student:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def __eq__(self, other):
return self.age == other.age
stu_list = []
student = Student("周杰伦", 11, "长沙")
student2 = Student("林俊杰", 11, "武汉")
print(student == student2) # True
2
3
4
5
6
7
8
9
10
11
12
13
14
# 封装
# 面向对象的三大特性
面向对象编程,是许多编程语言都支持的一种编程思想。
简单理解是:基于模板(类)去创建实体(对象),使用对象完成功能开发。
面向对象包含3打主要特性:
- 封装
- 继承
- 多态
# 私有成员
类中提供了私有成员的形式来支持
- 私有成员变量: 变量名以__开头(两个下划线)
- 私有成员方法: 方法名以__开头(两个下划线)
私有方法无法直接被类对象使用,私有变量无法赋值也无法取值
class Phone:
__current_voltage = None # 当前手机运行电压
def __keep_single_core(self):
print("让CPU以单核模式运行")
phone = Phone()
phone.__keep_single_core() # 报错!
2
3
4
5
6
7
8
注意
私有成员无法被类对象使用,但是可以被其他的成员使用。
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()
# 5g关闭,使用4g网络
# 正在通话中
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 继承
继承分为:单继承和多继承。
继承表示:将从父类那里继承(复制)来成员变量和成员方法(不含私有)
# 单继承
class Phone:
IMEI = None # 序列号
producer = None # 厂商
def call_by_4g(self):
print("4g通话")
class Phone2022(Phone):
face_id = True
def call_by_5g(self):
print("2022最新5g通话")
phone = Phone2022()
phone.call_by_4g() # 4g通话
phone.call_by_5g() # 2022最新5g通话
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 多继承
语法:
class 类名(父类1, 父类2, ..., 父类N):
类内容体
class Phone:
IMEI = None # 序列号
producer = "HM" # 厂商
def call_by_4g(self):
print("4g通话")
class NFCReader:
nfc_type = "第五代"
producer = "ITCAST"
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_4g() # 4g通话
phone.read_card() # NFC读卡
phone.write_card() # NFC写卡
phone.control() # 红外遥控开启了
print(phone.producer) # HM
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
pass关键字的作用
pass是一个普通的占位语句,他用来保证我们的函数或者方法,或者说类定义的完整性,表示无内容,空的意思
多继承注意事项
多个父类中,如果有同名成员,那么默认以继承顺序(从左到右)为优先级,即:先继承的保留,后继承的被覆盖
# 复写
子类继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写。
即:在子类中重新定义同名的属性或方法即可。
class Phone:
IMEI = None # 序列号
producer = "HM" # 厂商
def call_by_4g(self):
print("4g通话")
class MyPhone(Phone):
producer = "ME"
def call_by_4g(self):
print("子类的call")
phone = MyPhone()
phone.call_by_4g() # 子类的call
print(phone.producer) # ME
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 调用父类同名成员
一旦复写父类成员,那么类的对象调用成员的时候,就会调用复写后的新成员,如果需要使用被复写的父类的成员,需要特殊的调用方式:
- 方式一: 调用父类成员
使用成员变量: 父类名.成员变量
使用成员方法: 父类名.成员方法(self)
class Phone:
IMEI = None # 序列号
producer = "HM" # 厂商
def call_by_4g(self):
print("4g通话")
class MyPhone(Phone):
producer = "ME"
def call_by_4g(self):
print("子类的call")
# 方式1
Phone.call_by_4g(self)
print(f"父类的厂商是{Phone.producer}")
phone = MyPhone()
phone.call_by_4g()
# 子类的call
# 4g通话
# 父类的厂商是HM
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 方式二: 使用super()调用父类成员
使用成员变量: super().成员变量
使用成员方法: super().成员方法()
class Phone:
IMEI = None # 序列号
producer = "HM" # 厂商
def call_by_4g(self):
print("4g通话")
class MyPhone(Phone):
producer = "ME"
def call_by_4g(self):
print("子类的call")
# 方式2
super().call_by_4g()
print(f"父类的厂商是{super().producer}")
phone = MyPhone()
phone.call_by_4g()
# 子类的call
# 4g通话
# 父类的厂商是HM
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 类型注解
# 类型注解
Python在3.5版本的时候引入了类型注解,以方便静态类型检查工具,IDE等第三方工具。
类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)。
主要功能:
- 帮助第三方IDE工具对代码进行类型推断,协助做代码提示
- 帮助开发者自身对变量进行类型注释
支持:
- 变量的类型注解
- 函数(方法)形参列表和返回值的类型注解
并不会真正的对类型做验证和判断。
也就是,类型注解仅仅是提示性的,不是决定性的。
# 类型注解语法
# 为变量设置类型注解
基础语法:
变量:类型
- 基础数据类型注解
var_1: int = 10
var_2: float = 3.1415926
var_3: bool = True
var_4: str = "itheima"
2
3
4
- 类对象类型注解
class Student:
pass
stu: Student = Student()
2
3
4
- 基础容器类型注解
my_list: list = [1, 2, 3]
my_tuple: tuple = (1, 2, 3)
my_set: set = {1, 2, 3}
my_dict: dict = {"itheima": 666}
my_str: str = "itheima"
2
3
4
5
- 容器类型详细注解
my_list: list[int] = [1, 2, 3]
my_tuple: tuple[str, int, bool] = ("itheima", 666, True)
my_set: set[int] = {1, 2, 3}
my_dict: dict[str, int] = {"itheima": 666}
2
3
4
注意
- 元组类型设置类型详细注解,需要将每个元素都标记出来
- 字典类型设置类型详细注解,需要2个类型,第一个是key,第二个是value
- 在注释中进行类型注解
语法:# type: 类型
import random
import json
class Student:
pass
data = '{"name":123}'
var_1 = random.randint(1, 10) # type: int
var_2 = json.loads(data) # type: dict[str, int]
var_3 = Student() # type: Student
2
3
4
5
6
7
8
9
10
11
12
13
# 函数(方法)的类型注解
- 形参注解
def 函数方法名(形参名:类型, 形参名:类型,.....):
pass
2
- 返回值注解
def 函数方法名(形参名:类型, 形参名:类型,.....) -> 返回值类型:
pass
2
def add(x:int, y:int) -> int:
return x + y
2
# Union类型
from typing import Union
my_list: list[Union[str, int]] = [1, 2, "itheima", "itcast"]
my_dict: dict[str, Union[str, int]] = {"name":"周杰伦", "age": 13}
2
3
4
使用Union[类型,...类型],可以定义联合类型注解。
# 多态
多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。
def make_noise(animal: Animal):
animal.speak()
dog = Dog()
cat = Cat()
make_noise(dog) # 输出:汪汪汪
make_noise(cat) # 输出:喵喵喵
2
3
4
5
6
7
8
同样的行为(函数),传入不同的对象,得到不同的状态。
# 抽象类(接口)
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("汪汪汪")
class Cat(Animal):
def speak(self):
print("喵喵喵")
2
3
4
5
6
7
8
9
10
11
父类Animal的speak方法,是空实现,这种设计的含义是:
- 父类用来确定有哪些方法
- 具体的方法实现,由子类自行决定
这种写法,就叫做抽象类(也可以称之为接口)
抽象类: 含有抽象方法的类称之为抽象类
抽象方法: 方法体是空实现的(pass)称之为抽象方法。
抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现。