当前位置: 首页 > news >正文

【云运维】Python基础(三)

Python基础(三)

第7章:文件与数据格式化

程序运行时的临时数据会随程序结束消失,而游戏角色属性、用户配置等数据需长期保存。Python 中通过文件操作实现数据持久化,

7.1:文件概述:计算机中的数据存储单元

文件是计算机中以外部介质(如硬盘)为载体的数据集合,文本文档、图片、程序等均属于文件,操作系统以文件为单位管理数据。

7.1.1 文件标识:找到唯一文件的 “地址”

文件标识用于定位计算机中唯一文件,由三部分组成:

  • 文件路径:文件在存储介质中的位置(如 f:\a.txt
  • 文件名主干:文件的核心名称(如 a
  • 文件扩展名:标识文件类型(如 .txt.png

在这里插入图片描述

7.1.2 文件类型:按逻辑结构分类

文件按数据逻辑存储结构分为两类,物理层面均以二进制形式存储:

  • 文本文件:存储文本字符数据,可通过记事本直接读写
  • 二进制文件:需遵循特定序列化 / 反序列化规则,无法用普通文字处理程序直接打开(如图片、可执行程序)
7.1.3 Python 标准文件

sys 模块内置 3 个标准文件,对应输入输出设备:

  • stdin:标准输入文件,关联键盘等输入设备
  • stdout:标准输出文件,关联显示器等输出设备
  • stderr:标准错误文件,关联显示器等输出设备
import sys
sys.stdout.write('lucky cloud')  # 直接输出到显示器

7.2:文件基础操作:打开、关闭与读写

文件的核心操作包括打开、关闭、读写,所有复杂操作均基于这些基础动作,关键在于控制打开模式和正确处理资源。

7.2.1 文件打开模式:控制操作权限

open () 函数用于打开文件,返回文件对象,核心参数 mode 决定操作权限,常见模式如下:

打开模式名称描述
r/rb只读模式只读打开文本 / 二进制文件,文件不存在则报错
w/wb只写模式只写打开文本 / 二进制文件,文件存在则覆盖,不存在则创建
a/ab追加模式只写打开文本 / 二进制文件,仅在文件末尾追加数据,不存在则创建
r+/rb+读取(更新)模式可读可写打开文本 / 二进制文件,文件不存在则报错
w+/wb+写入(更新)模式可读可写打开文本 / 二进制文件,文件存在则覆盖,不存在则创建
a+/ab+追加(更新)模式可读可写打开文本 / 二进制文件,仅在末尾追加数据,不存在则创建
7.2.2 打开与关闭文件:资源管理关键
  • 打开文件:使用 open(file, mode='r', buffering=None) 函数,file 为文件路径
  • 关闭文件:必须及时关闭以释放资源,避免文件句柄耗尽或数据丢失

两种关闭方式

  1. close () 方法:文件对象的内置方法,需手动调用
file = open('f:\\a.txt', 'r')
file.close()  # 手动关闭文件
  1. with 语句:自动关闭文件,推荐使用(无需手动调用 close ())
with open('f:\\a.txt', 'r') as f:# 文件操作代码pass  # 代码块结束后自动关闭文件

==思考:==为什么要及时关闭文件?

  1. 计算机中可打开的文件数量是有限。
  2. 打开的文件占用系统资源。
  3. 若程序因异常关闭,可能产生数据丢失。
7.2.3 文件读取:三种核心方法

Python 提供 read ()、readline ()、readlines () 三种读取方法,适用于不同场景:

1. read (size):读取指定字节数

  • size 为读取字节数,默认读取全部数据
  • 大文件不建议省略 size,避免耗尽内存

示例:读取f盘中luckycloud.txt的数据,代码示例如下:

方法一:

with open('f:\\luckycloud.txt', 'r', encoding='utf-8') as f:print(f.read(2))  # 读取前2个字节print(f.read())   # 读取剩余全部数据

方法二:

file = open('f:\\luckycloud.txt','r')
connect = file.read(2)
print(connect)
print('-'*30)
connect = file.read()
print(connect)
file.close()

2. readline ():逐行读取

  • 每次读取一行数据,换行符 \n 会被保留
  • 适合大文件逐行处理,避免内存占用过高
with open('f:\\luckycloud.txt', 'r') as f:print(f.readline())print(f.readline())

运行结果:

actions speak louder than wordsluckycloud is here

3. readlines (hint):读取所有行并返回列表

readlines()方法可以一次读取文件中的所有数据,若读取成功,该方法会返回一个列表,文件中的每一行对应列表中的一个元素。语法格式如下:

readlines(hint=-1)
  • hint 控制读取行数(按字节总数限制),默认读取全部行

  • hint:单位为字节,用于控制要读取的行数如果行中数据的总大小超出了hint字节,readlines()不会再读取更多的行。

  • 返回列表,每行数据为列表元素,大文件慎用

  • 读取文件时若出现编码错误,可使用 chardet 库自动识别编码:

import chardet
# 先以二进制模式读取文件,检测编码
with open('f:\\luckycloud.txt', 'rb') as f:raw_data = f.read()encoding = chardet.detect(raw_data)['encoding']   # 获取编码格式
# 按检测到的编码读取文件
with open('f:\\luckycloud.txt', 'r', encoding=encoding, errors='ignore') as f:print(f.readlines())

运行结果:

['actions speak louder than words\n', 'luckycloud is here\n']

总结:

  • read()(参数缺省时)和readlines()方法都可一次读取文件中的全部数据,但因为计算机的内存是有限的,若文件较大,read()和readlines()的一次读取便会耗尽系统内存,所以这两种操作都不够安全。

  • 为了保证读取安全,通常多次调用read()方法,每次读取size字节的数据。

7.2.4 文件写入:两种核心方法

写入文件需注意打开模式(w 覆盖、a 追加),核心方法为 write () 和 writelines ():

1. write (data):写入字符串
  • 接收字符串参数,返回写入的字节数
  • 适用于单行数据写入
with open('f:\\luckycloud.txt', 'w', encoding='utf-8') as f:size = f.write('I am planning to write Python')  # 写入字符串print(size)  # 输出写入的字节数:29
2. writelines (lines):写入字符串序列
  • 接收字符串或字符串列表,无返回值
  • 需显式添加换行符 \n,适用于多行数据写入
with open('f:\\luckycloud.txt', 'w', encoding='utf-8') as f:lines = ['Life is short\n', 'I use python']  # 字符串列表f.writelines(lines)  # 按行写入,需手动加换行符
写入方法对比
  • write ():仅接收字符串,单行写入更简洁
  • writelines ():接收字符串列表,多行写入更高效,需手动处理换行
7.2.5 字符编码:文本文件的底层规则

不同编码方式下,字符与字节的对应关系不同,常见编码及特点:

编码方式支持语言中文字节数英文字节数
ASCII英文不支持1
UTF-8多语言31
Unicode多语言22
GBK中 / 英文21
7.2.6:文件定位读写:控制读写位置

read()方法读取了文件luckycloud.txt,结合代码与程序运行结果进行分析,可以发现read()方法第1次读取了2个字符,第2次从第3个字符开始读取了剩余字符。

  • 在文件的一次打开与关闭之间进行的读写操作是连续的,程序总是从上次读写的位置继续向下进行读写操作。

  • 每个文件对象都有一个称为“文件读写位置”的属性,该属性会记录当前读写的位置。

  • 文件读写位置默认为0,即在文件首部。

Python提供了一些获取与修改文件读写位置的方法,以实现文件的定位读写:

  • tell():获取文件当前的读写位置。

  • seek():控制文件的读写位置。

1) tell ():获取当前读写位置

tell()方法用于获取文件当前的读写位置,以操作文件lucky.txt为例,tell()的用法如下:

with open('f:\\lucky.txt') as f:print(f.tell())        #获取文件读写位置print(f.read(5))       #利用位置read()方法移动文件读写位置print(f.tell())        #再次获取文件读写位置

运行结果:

0
actio
5
2 ) seek (offset, from):移动读写位置

Python提供了seek()方法,使用该方法可控制文件的读写位置,实现文件的随机读写。

  • offset:表示偏移量,即读写位置需要移动的字节数。
  • from:用于指定文件的读写位置,该参数的取值为0、1、2。
    • 0:表示文件开头
    • 1:表示使用当前读写位置
    • 2:表示文件末尾
  • seek()方法调用成功后会返回当前读写位置。
  • 注意:文本文件仅支持 from=0,二进制文件支持所有参考位置
# 二进制文件示例
with open('f:\\lucky.txt') as f:f.tell()                 # 获取文件读写位置sep = f.seek(5,0)        # 相对文件首部移动5字节print(sep)               # 输出当前文件读写位置

运行结果:

5

7.3:文件与目录管理:os 模块的核心用法

对于用户而言,文件和目录以不同的形式展现,但对计算机而言,目录是文件属性信息集合,它本质上也是一种文件。

os 模块提供文件和目录的管理功能,包括删除文件、创建目录、获取文件列表等。

7.3.1 文件管理
  • **删除文件:**remove (文件路径),文件不存在则报错
import osos.remove('f:\\lucky.txt')  # 删除指定文件
  • **重命名文件:**rename (原路径,新路径)
import osos.rename('f:\\luckycloud.txt', 'f:\\cloud.txt')  # 文件名修改
7.3.2 目录管理
  • **获取当前目录:**getcwd (),返回绝对路径
import osprint(os.getcwd())  # 示例输出:F:\PycharmProjects\pythonProject1\pycode\文件操作
  • **创建目录:**mkdir (目录路径),目录已存在则报错
import osos.mkdir('f:\\abc')  # 在F盘创建abc目录
  • **删除目录:**rmdir (目录路径),目录非空则报错
import osos.rmdir('f:\\abc')  # 删除abc目录
  • **更改默认目录:**chdir (目录路径),修改 Python 工作目录
import osprint('更改前默认路径:',os.getcwd())
os.chdir('f:\\')  # 将默认目录改为F盘
print('更改后默认路径:',os.getcwd())# 运行结果
更改前默认路径: F:\PycharmProjects\pythonProject1\pycode\文件操作
更改后默认路径: f:\
  • **获取目录文件列表:**listdir (目录路径),返回文件名列表
import osfiles = os.listdir('f:\\')  # 获取F盘所有文件/目录名称
print(files)  # 输出格式:['文件1.txt', '目录1', ...]

7.4:数据维度与数据格式化

数据维度和格式化是 Python 数据处理的核心基础,清晰的维度划分能让数据组织更有序,规范的格式化则是数据存储、交换的关键。

7.4.1 基于维度的数据分类

维度本质是与数据相关联的参数数量,不同维度对应不同的数据组织形式,Python 中也有专属的数据结构支持。

1 )一维数据:线性对等的基础数据

  • 定义:具有对等关系的线性数据集合,仅需一个参数即可描述。
  • 对应 Python 数据结构:列表(list)、元组(tuple)、集合(set)。
  • 示例:["成都", "杭州", "重庆"](10, 20, 30){"a", "b", "c"}

2)二维数据:表格化的结构化数据

  • 定义:关联两个参数的数据集合,可理解为 “一维数据的集合”,呈现表格形态(行 × 列)。
  • 对应 Python 数据结构:矩阵、二维数组、二维列表、二维元组。
  • 示例:二维数据如下
姓名语文数学英语理综
刘婧124137145260
张昭华116143139263
邢昭林120130148255
鞠依依115145131240
黄丽萍123108121235
赵越132100112210

3) 多维数据:复杂关联的嵌套数据

  • 定义:关联三个及以上参数,需通过层级关系展示复杂结构。
  • 核心特征:用**键值对(key-value)**表达层级关联。
  • 对应 Python 数据结构:字典(dict),网络传输中常用 JSON 格式。
  • 示例:
 "高三一班考试成绩": [{"姓名": "刘婧","语文": "124","数学": "137","英语": "145","理综": "260"},{"姓名": "张华","语文": "116","数学": "143","英语": "139","理综": "263"}..........]
7.4.2 一二维数据的存储与读写实战

数据通常存储在文件中,规范的存储格式是高效读写的前提,其中 CSV 是一二维数据的通用标准。

7.4.2.1 存储格式:约定分隔规则

(1)一维数据存储

  • 核心原则:用统一的特殊字符分隔数据,避免歧义。
  • 常用分隔符:空格、逗号(,)、&(需满足 “分隔符不出现在数据中”)。
  • 格式要求:使用英文半角符号,同一文件 / 文件组统一分隔符。
  • 示例:
    • 逗号分隔:成都,杭州,重庆,武汉
    • 空格分隔:10 20 30 40

(2)二维数据存储:CSV 格式详解

  • 二维数据可视为多条一维数据的集合,当二维数据只有一个元素时,这个二维数据就是一维数据。
  • CSV(Comma-Separated Values,逗号分隔值):国际通用的表格数据存储格式,纯文本形式
  • 格式规范:
    1. 每一行对应一条数据记录;
    2. 每条记录的字段用英文半角逗号分隔;
    3. 每条记录由一个或多个字段组成;
    4. Windows平台中CSV文件的后缀名为.csv,可通过Office Excel或记事本打开。
  • 示例(学生成绩 CSV):
姓名,语文,数学,英语,理综
刘备,124,137,145,260
张飞,116,143,139,263
关羽,120,130,148,255
周瑜,115,145,131,240
诸葛亮,123,108,121,235
黄月英,132,100,112,210

注意:

CSV广泛应用于不同体系结构下网络应用程序之间表格信息的交换中,它本身并无明确格式标准,具体标准一般由传输双方协议决定。

7.4.2.2 数据读取:Python 解析 CSV 文件

Python 读取 CSV 文件后,默认以二维列表形式存储,需处理编码问题避免乱码。

实战代码:读取 CSV 并转换为二维列表

import chardet# 自动检测文件编码(解决中文乱码问题)
with open('f:\\score.csv', 'rb') as f:raw_data = f.read()encoding = chardet.detect(raw_data)['encoding']# 读取CSV文件
with open('f:\\score.csv', encoding=encoding, errors='ignore') as f:lines = []for line in f:line = line.replace('\n', '')  # 去除换行符lines.append(line.split(','))  # 按逗号分隔字段print("读取结果(二维列表):")
print(lines)

运行结果:

[['姓名', '语文', '数学', '英语', '理综'],['刘备', '124', '137', '145', '260'],['张飞', '116', '143', '139', '263'],['关羽', '120', '130', '148', '255']
]
7.4.2.3 数据写入:向 CSV 添加新字段

将一、二维数据写入文件中,即按照数据的组织形式,在文件中添加新的数据。

在已有 CSV 文件中新增数据(如学生总分),需按 CSV 格式规则拼接字段。

实战代码:计算学生总分并写入新文件

import chardet# 自动检测编码
def get_file_encoding(file_path):with open(file_path, 'rb') as f:raw_data = f.read()return chardet.detect(raw_data)['encoding']# 读取原始成绩文件
input_path = 'f:\\score.csv'
output_path = 'f:\\count.csv'
encoding = get_file_encoding(input_path)with open(input_path, encoding=encoding, errors='ignore') as f:# 打开新文件用于写入(w+模式:读写+创建)with open(output_path, 'w+', encoding='utf-8') as new_file:lines = []# 读取并处理每一行for line in f:line = line.replace('\n', '')lines.append(line.split(','))# 1. 添加表头字段“total”lines[0].append('total')# 2. 计算每个学生的总分(跳过表头)for i in range(1, len(lines)):total = 0# 遍历当前学生的所有科目成绩for item in lines[i]:if item.isnumeric():  # 判断是否为数字(排除姓名字段)total += int(item)lines[i].append(str(total))  # 总分转为字符串添加到列表# 3. 写入新文件(按CSV格式拼接)for line in lines:new_file.write(','.join(line) + '\n')print(line)  # 打印验证结果

运行结果(使用Excel打开文件):

姓名语文数学英语理综总计
刘备124137145260666
张飞116143139263661
关羽120130148255653
周瑜115145131240631
诸葛亮123108121235587
黄月英132100112210554

关键说明:

  • isnumeric():判断字符串是否仅包含数字,用于区分 “姓名” 和 “成绩” 字段。
  • ','.join(line):将列表元素用逗号拼接为 CSV 格式的字符串。
  • 输出文件编码设为utf-8,避免中文乱码。
7.4.3 多维数据格式化:JSON 核心应用

三维及以上的多维数据,需用键值对形式格式化,JSON 是网络传输中最常用的格式(比 XML、HTML 更简洁、省流量)。

7.4.3.1 JSON 格式语法规则
  • 数据存储在键值对中:"key": "value"(key 必须用双引号)。
  • 字段用逗号分隔:"姓名": "张飞", "语文": 116
  • 花括号{}表示一个 JSON 对象:{"姓名": "张飞", "成绩": 561}
  • 方括号[]表示一个数组(多个对象集合):[{"姓名": "张飞"}, {"姓名": "关羽"}]
7.4.3.2 JSON 与 Python 对象的转换

Python 的json模块提供dumps()loads()两个核心函数,实现 Python 对象与 JSON 字符串的双向转换。

函数功能描述
dumps()对 Python 对象进行转码,将其转化为 JSON 字符串
loads()将 JSON 字符串解析为 Python 对象

(1)类型对应关系

Python 对象JSON 数据类型
dictobject
list/tuplearray
strstring
int/floatnumber
Truetrue
Falsefalse
Nonenull

(2)实战:Python 对象转 JSON(dumps ())

import json# 定义Python复杂对象(包含列表、字典、布尔值等)
py_obj = [[1, 2, 3],10, 3.14,'tom',{'java': 98, 'python': 100},True, False, None
]# 转换为JSON字符串
json_str = json.dumps(py_obj)
print(json_str)

运行结果:

[[1, 2, 3], 10, 3.14, "tom", {"java": 98, "python": 100}, true, false, null]

(3)实战:JSON 转 Python 对象(loads ())

import json# 承接上面的JSON字符串
json_str = '[[1, 2, 3], 10, 3.14, "tom", {"java": 98, "python": 100}, true, false, null]'
print(json_str)# 解析JSON字符串为Python对象
py_new = json.loads(json_str)
print(py_new)
# print("类型验证:", type(py_new))  # 输出:<class 'list'>
# print("字典元素访问:", py_new[5]['python'])  # 输出:100

运行结果:

[[1, 2, 3], 10, 3, 14, "tom", {"java": 98, "python": 100}, true, false, null]
[[1, 2, 3], 10, 3, 14, 'tom', {'java': 98, 'python': 100}, True, False, None]
7.4.3.3 多维数据格式化示例

(1)JSON 格式(学生成绩多维数据)

{"班级考试成绩": [{"姓名": "王小天","语文": 124,"数学": 127,"英语": 145,"理综": 259},{"姓名": "张大同","语文": 116,"数学": 143,"英语": 119,"理综": 273}]
}

(2)对比 XML 格式(同一数据)

<班级考试成绩><学生><姓名>王小天</姓名><语文>124</语文><数学>127</数学><英语>145</英语><理综>259</理综></学生><学生><姓名>张大同</姓名><语文>116</语文><数学>143</数学><英语>119</英语><理综>273</理综></学生>
</班级考试成绩>

优势总结:

  • JSON 更简洁,无需冗余标签;
  • 键(如 “姓名”“语文”)仅定义一次,网络传输时流量消耗更少;
  • 与 Python 字典天然兼容,解析效率更高。

第8章:面向对象

面向对象(OOP)是程序开发的核心思想之一,它模拟人类认识世界的思维方式,将复杂问题拆解为多个 “对象”,通过对象的交互解决问题。相比传统的面向过程编程,OOP 更适合大型项目开发,具备封装、继承、多态三大特性。

8.1:面向对象 vs 面向过程:核心区别

8.1.1 两种编程范式的本质
编程方式核心思想关注点优点缺点
面向过程按步骤解决问题步骤、函数简单直接、执行效率高代码复用性差、维护困难
面向对象抽象为对象交互对象、属性、方法易扩展、易维护、复用性强入门门槛高、执行效率略低
8.1.2 实例对比:制作一杯咖啡

🌰 面向过程方式(按步骤执行)

  • 准备材料:水、咖啡粉

  • 水壶加热水至沸腾→冷却至 90°C

  • 滤杯放滤纸,预热杯子

  • 咖啡粉放入滤纸

  • 缓慢倒水冲泡

  • 过滤完成,得到咖啡

🌰 面向对象方式(对象交互)

  • 核心对象:咖啡机、水、咖啡粉、杯子

  • 对象的属性与方法

  • 咖啡机:添加原料、煮制咖啡

  • 水:设置加热温度

  • 咖啡粉:设置研磨程度

  • 杯子:接收咖啡液

  • 灵活扩展:想喝茶时,只需将 “咖啡粉” 对象替换为 “茶叶”,无需修改其他流程

8.2:类与对象:OOP 的核心概念

8.2.1 类与对象的关系
  • :具有相同属性和行为的事物的抽象集合(如 “汽车类”)

  • 对象:类的具体实例(如 “我的特斯拉 Model 3”)

  • 通俗理解:类是 “模板”,对象是 “根据模板创建的产品”

在这里插入图片描述

8.2.2 类的定义(语法 + 规范)
类的三要素
  1. 类名:采用大驼峰命名法(首字母大写,如Person、CoffeeMachine)

  2. 属性:描述事物的静态特征(如姓名、颜色、车轮数)

  3. 方法:描述事物的动态行为(如行驶、冲泡、加热)

基础语法
class 类名:# 1. 类属性(声明在方法外部)类属性名 = 属性值# 2. 方法(声明在类内部,类似函数)def 方法名(self, 参数...):方法体# 实例属性(声明在方法内部,用self修饰)self.实例属性名 = 属性值

示例:定义 “汽车类”

class Car:# 类属性:所有汽车共享的特征wheels = 4  # 车轮数(默认4个)# 实例方法:汽车的行为def drive(self):# 实例属性:每个对象独有的特征(可动态添加)self.speed = 0print("汽车开始行驶")def accelerate(self, increment):self.speed += incrementprint(f"加速至 {self.speed} km/h")
8.2.3 对象的创建与使用

1) 创建对象(实例化)

# 语法:对象名 = 类名()
my_car = Car()  # 创建Car类的实例my_car

2.)访问对象成员(属性 + 方法)

# 访问类属性(类和对象均可访问)
print(Car.wheels)    # 输出:4(通过类访问)
print(my_car.wheels) # 输出:4(通过对象访问)# 调用实例方法(只能通过对象调用)
my_car.drive()       # 输出:汽车开始行驶
my_car.accelerate(60)# 输出:加速至 60 km/h# 访问实例属性(只能通过对象访问)
print(my_car.speed)  # 输出:60

8.3:类的成员:属性与方法详解

8.3.1 属性:类属性 vs 实例属性
类型声明位置访问方式修改方式作用域
类属性类内部、方法外部类。属性 / 对象。属性只能通过类修改所有对象共享
实例属性方法内部(self 修饰)只能通过对象访问对象。属性 = 新值仅当前对象

代码示例:属性的使用与修改

class Car:wheels = 4  # 类属性(所有汽车共享)def __init__(self):self.color = "红色"  # 实例属性(每个对象独有)# 1. 访问属性
my_car1 = Car()
print(Car.wheels)      # 类访问类属性 → 4
print(my_car1.wheels)  # 对象访问类属性 → 4
print(my_car1.color)   # 对象访问实例属性 → 红色# 2. 修改属性
Car.wheels = 3         # 类修改类属性(影响所有对象)
my_car1.wheels = 5     # 对象"影子修改"类属性(仅影响当前对象)
my_car1.color = "蓝色" # 对象修改实例属性# 验证结果
my_car2 = Car()
print(Car.wheels)      # → 3(类属性已被修改)
print(my_car1.wheels)  # → 5(当前对象的修改)
print(my_car2.wheels)  # → 3(新对象继承修改后的类属性)
print(my_car1.color)   # → 蓝色(实例属性修改)
print(my_car2.color)   # → 红色(新对象的默认实例属性)
8.3.2 方法:实例方法 vs 类方法 vs 静态方法
类型装饰器第一个参数调用方式作用
实例方法self(代表对象)只能对象调用操作实例属性、实现对象行为
类方法@classmethodcls(代表类)类 / 对象均可调用操作类属性、创建实例
静态方法@staticmethod无默认参数类 / 对象均可调用独立功能(不依赖类 / 对象属性)

代码示例:三种方法的使用

class Car:wheels = 4  # 类属性# 1. 实例方法def drive(self, speed):print(f"汽车以{speed}km/h行驶")# 2. 类方法(用cls访问类属性)@classmethoddef change_wheels(cls, num):cls.wheels = numprint(f"车轮数修改为:{cls.wheels}")# 3. 静态方法(独立功能)@staticmethoddef check_safety():print("正在进行安全检查...")# 调用方法
my_car = Car()
my_car.drive(80)          # 实例方法 → 汽车以80km/h行驶
Car.change_wheels(6)      # 类方法 → 车轮数修改为:6
my_car.check_safety()     # 静态方法 → 正在进行安全检查...
8.3.3 私有成员:数据安全的保障
1) 为什么需要私有成员?

类的公有成员可以被外部随意访问和修改,可能导致数据混乱。私有成员限制外部访问,仅允许类内部使用,提升代码安全性。

定义方式:属性 / 方法名前加双下划线(__)

class Car:__engine = "V8"  # 私有类属性wheels = 4       # 公有类属性def __start(self):  # 私有方法print(f"启动{self.__engine}发动机")# 公有方法(内部访问私有成员)def run(self):self.__start()print("汽车正常行驶")# 测试
my_car = Car()
my_car.run()  # 输出:启动V8发动机 → 汽车正常行驶# 外部访问私有成员(报错)
print(my_car.__engine)  # AttributeError
my_car.__start()        # AttributeError

注意:

核心要点:类是抽象模板,对象是具体实例;属性描述特征,方法描述行为。

易错点

  • 实例属性必须通过self声明,且只能通过对象访问。

  • 类方法用cls访问类属性,静态方法不能直接访问类 / 实例属性。

  • 私有成员不能外部直接访问,需通过公有方法间接操作。

8.4:特殊方法:构造与析构

8.4.1 构造方法 init()
  • 作用:创建对象时自动调用,用于初始化对象属性

  • 分类:无参构造、有参构造

示例 1:无参构造(固定初始值)

class Car:def __init__(self):self.color = "红色"  # 所有对象默认红色self.speed = 0my_car = Car()
print(my_car.color)  # → 红色

示例 2:有参构造(动态初始化)

class Car:def __init__(self, color, speed=0):self.color = color  # 传入颜色self.speed = speed  # 可选参数(默认0)# 创建不同属性的对象
car1 = Car("蓝色")
car2 = Car("黑色", 50)
print(car1.color, car1.speed)  # → 蓝色 0
print(car2.color, car2.speed)  # → 黑色 50
8.4.2 析构方法 del()
  • 作用:对象被销毁时自动调用,用于释放资源(如关闭文件、断开数据库连接)

  • 对象销毁时机:Python 通过 “引用计数器” 管理内存,当对象引用数为 0 时,系统自动销毁对象

示例:析构方法的使用

class Car:def __init__(self, name):self.name = nameprint(f"{self.name}已创建")def __del__(self):print(f"{self.name}已销毁(释放内存)")# 测试
car = Car("特斯拉")  # → 特斯拉已创建
del car  # 手动销毁对象 → 特斯拉已销毁(释放内存)

**扩展:**与文件类似,每个对象都会占用系统的一块内存,使用之后若不及时销毁,会浪费系统资源。那
么对象什么时候销毁呢?
**答:**Python通过引用计数器记录所有对象的引用(可以理解为对象所占内存的别名)数量,一旦某个对象的
引用计数器的值为0,系统就会销毁这个对象,收回对象所占用的内存空间。

8.5:封装:隐藏细节,安全访问

封装是面向对象的基础特性,核心思想是隐藏类的内部实现细节,仅提供公开接口供外部访问。这样既保护了类内数据的安全性,也降低了外部使用类的复杂度(无需关注内部逻辑)。

实现要求

  1. 类的属性声明为私有属性(Python 中用双下划线 __ 开头标识)。
  2. 提供两类公有方法(get_xxx()set_xxx()),分别用于获取和修改私有属性的值。

代码示例

class Person:def __init__(self, name):self.name = name  # 公有属性(姓名)self.__age = 1    # 私有属性(年龄,默认1岁)# 公有方法:设置年龄(含合法性校验)def set_age(self, new_age):if 0 < new_age <= 120:  # 限制年龄范围,保证数据安全self.__age = new_age# 公有方法:获取年龄def get_age(self):return self.__age# 外部使用:仅通过公开接口操作,无需关注内部实现
person = Person("杰瑞")
person.set_age(22)
print(f"姓名是{person.name},年龄为{person.get_age()}岁")  # 输出:姓名是杰瑞,年龄为22岁

核心优势:

  • 数据安全:避免外部直接修改属性导致的非法值(如年龄设为 200)。
  • 低耦合:外部与类的内部实现解耦,后续修改内部逻辑不影响外部使用。

8.6:继承:复用代码,扩展功能

继承用于描述类与类的 “从属关系”,核心是在不修改原有类的基础上,复用其代码并扩展新功能。被继承的类称为 “父类(基类)”,继承的类称为 “子类(派生类)”,子类会自动拥有父类的公有成员(属性和方法)。

8.6.1. 单继承:子类仅继承一个父类

单继承是最常见的继承形式,子类只关联一个父类。。现实生活中,波斯猫、折耳猫、短毛猫都属于猫类,它们之间存在的继承关系即为单继承,如图所示。

在这里插入图片描述

语法格式

class 子类名(父类名):# 子类自身的属性和方法(可选)pass

代码示例

# 父类:猫类
class Cat(object):def __init__(self, color):self.color = color  # 公有属性:颜色self.__age = 1     # 私有属性:年龄(子类无法直接访问)# 公有方法:猫的行为def walk(self):print("走猫步")# 私有方法:子类无法直接调用def __test(self):print("父类私有方法")# 子类:折耳猫(继承自 Cat)
class ScottishFold(Cat):pass# 子类使用:自动继承父类的公有成员
fold = ScottishFold("灰色")
print(f"{fold.color}的折耳猫")  # 输出:灰色的折耳猫(访问父类公有属性)
fold.walk()                     # 输出:走猫步(调用父类公有方法)# 注意:子类不会拥有父类的私有成员,也不能访问父类的私有成员。
# 错误示范:子类无法访问父类私有成员
# print(fold.__age)  # 报错:AttributeError(无__age属性)
# fold.__test()      # 报错:AttributeError(无__test方法)
8.6.2. 多继承:子类继承多个父类

子类可以同时继承多个父类,自动拥有所有父类的公有成员。

语法格式

class 子类名(父类名1, 父类名2, ...):pass

代码示例(房车:继承房屋和汽车的功能)

# 父类1:房屋类
class House(object):def live(self):print("供人居住")# 父类2:汽车类
class Car(object):def drive(self):print("行驶")# 子类:房车(同时继承 House 和 Car)
class TouringCar(House, Car):pass# 子类使用:调用多个父类的方法
tour_car = TouringCar()
tour_car.live()  # 输出:供人居住(调用 House 类方法)
tour_car.drive() # 输出:行驶(调用 Car 类方法)

关键注意点(同名方法优先级)

如果多个父类有同名方法,子类会按照继承顺序优先调用先声明的父类方法(即如果子类继承的多个父类是平行关系的类,那么子类先继承哪个类,便会先调用哪个类的方法。)。

8.6.3. 重写:子类自定义父类方法

子类会原封不动继承父类方法,但如果需要适配自身需求,可以在子类中定义与父类同名的方法,覆盖父类方法(即 “重写”)。

代码示例(重写 + 调用父类方法)

# 父类:人类
class Person(object):def say_hello(self):print("打招呼!")# 子类:中国人(重写父类方法)
class Chinese(Person):def say_hello(self):# 子类重写了父类的方法之后,无法直接访问父类的同名方法,但可以使用super()函数间接调用父类中           被重写的方法。super().say_hello()  print("阿吃过啦")     # 子类自定义逻辑# 调用结果:优先执行子类重写后的方法
chinese = Chinese()
chinese.say_hello()
# 输出:
# 打招呼!
# 阿吃过啦

8.7:多态:同一接口,不同行为

多态是面向对象的灵活特性,核心是让不同类的同一功能,通过同一个接口调用时表现出不同行为。其实现依赖 “继承 + 方法重写”。

核心逻辑

  1. 定义统一接口(函数或方法),接收 “父类类型” 的参数。
  2. 不同子类重写该接口对应的方法,实现自定义逻辑。
  3. 调用接口时,传入不同子类的实例,自动执行对应子类的方法。

代码示例(动物叫:不同动物有不同叫声)

# 父类:猫类
class Cat:def shout(self):print("喵喵喵~")# 父类:狗类(无显式继承,但遵循同一接口规范)
class Dog:def shout(self):print("汪汪汪!")# 统一接口:接收任意实现了 shout() 方法的对象
def animal_shout(obj):obj.shout()# 多态体现:同一接口调用不同对象,行为不同
cat = Cat()
dog = Dog()
animal_shout(cat)  # 输出:喵喵喵~
animal_shout(dog)  # 输出:汪汪汪!

核心优势

  • 灵活性高:新增子类(如 Bird 类)时,无需修改接口代码,直接复用。
  • 代码简洁:统一接口减少冗余,降低调用者的记忆成本。

8.8:运算符重载:赋予运算符新功能

运算符重载是 Python 的进阶特性,核心是重写基类 object 的特殊方法,让 +-* 等内置运算符能适配自定义类的实例。

常用运算符对应的特殊方法

运算符特殊方法功能描述
+__add__(self, other)加法运算
-__sub__(self, other)减法运算
*__mul__(self, other)乘法运算
/__truediv__(self, other)除法运算(真除法)

代码示例(自定义计算器类)

class Calculator(object):def __init__(self, number):self.number = number  # 初始数值# 重载 + 运算符def __add__(self, other):self.number += otherreturn self.number# 重载 - 运算符def __sub__(self, other):self.number -= otherreturn self.number# 重载 * 运算符def __mul__(self, other):self.number *= otherreturn self.number# 重载 / 运算符(注意:除数不能为0)def __truediv__(self, other):if other != 0:self.number /= otherreturn self.numberelse:raise ValueError("除数不能为0")# 使用示例
calc = Calculator(10)
print(calc + 5)   # 输出:15(调用 __add__)
print(calc - 3)   # 输出:12(调用 __sub__)
print(calc * 2)   # 输出:24(调用 __mul__)
print(calc / 4)   # 输出:6.0(调用 __truediv__)

小结:

面向对象的三大特性各有侧重:

  • 封装:保护数据,隐藏细节(“安全”);
  • 继承:复用代码,快速扩展(“高效”);
  • 多态:统一接口,灵活适配(“灵活”);
  • 运算符重载:扩展运算符功能,让自定义类更易用。

掌握这些特性后,能写出更易维护、可扩展的 Python 代码,尤其适合大型项目开发。

第9章:异常

程序开发和运行过程中,异常是无法避免的。它可能源于代码设计缺陷,也可能由外界环境变化(如文件缺失、输入错误)引发。若不加以处理,程序会直接终止并返回错误信息。

9.1:异常概述

异常是程序运行时的错误事件,会中断正常执行流程。当程序未设置异常处理机制时,Python 解释器会采用默认方式响应:输出异常信息(含行号、类型、描述)并终止程序。

9.1.1 核心异常类型

Python 中所有异常都继承自BaseException类,常用业务异常多继承自其子类Exception。以下是开发中高频遇到的异常类型:

  • NameError:使用未定义的变量或函数时触发。
  • IndexError:列表、元组等序列类型的索引越界访问时触发。
  • AttributeError:访问对象不存在的属性或方法时触发。
  • FileNotFoundError:尝试打开不存在的文件或目录时触发。
  • ZeroDivisionError:除数为 0 时触发(如5/0)。
  • ValueError:传入的值类型正确但内容无效(如int('abc'))。

在这里插入图片描述

9.1.2 异常信息示例

异常触发时,解释器输出的 Traceback 信息格式如下(以除零错误为例):

Traceback (most recent call last):File "E:\pyproject\异常\异常概念.py", line 1, in <module>print(5/0)
ZeroDivisionError: division by zero

信息包含三部分:错误发生的文件路径与行号、触发错误的代码、异常类型及描述。

9.2:异常捕获:try-except 相关语句

Python 提供try-except系列语句捕获并处理异常,避免程序意外终止。可根据需求选择基础捕获、组合子句等不同用法。

9.2.1 基础用法:try-except

核心作用是监控try块中的代码,当异常发生时执行except块的处理逻辑。

语法规则:

try:可能出错的代码
except [异常类型 [as error]]:         # 将捕获到的异常对象赋error捕获异常后的处理代码

try-except语句可以捕获与处理程序的单个、多个或全部异常。

(1)捕获单个异常

针对特定异常类型单独处理,精准定位问题。

num1 = int(input('请输入被除数:'))
num2 = int(input('请输入除数:'))
try:print('结果为', num1 / num2)
except ZeroDivisionError:              # 单个异常类型print('出错了:除数不能为0')

(2)捕获多个异常

用元组指定多个异常类型,统一处理同类错误。

# 如果输入的是字母进行异常捕捉
try:num1 = int(input('请输入被除数:'))num2 = int(input('请输入除数:'))print('结果为', num1 / num2)
except (ZeroDivisionError, ValueError) as error:          # 定义error变量输出异常内容print('出错了:', error)  # 输出具体异常描述

(3)捕获所有异常

通过Exception(所有业务异常的父类)捕获未明确指定的异常,适合兜底处理。

try:num1 = int(input('请输入被除数:'))num2 = int(input('请输入除数:'))print('结果为', num1 / num2)
except Exception as error:               # 父类异常print('出错了:', error)
9.2.2 组合用法:try-except-else

else子句在try块无异常时执行,用于处理正常流程的后续逻辑。

语法规则:

try:可能出错的代码
except [异常类型 [as error]]:                  # 将捕获到的异常对象赋值error捕获异常后的处理代码
else:未捕获异常后的处理代码

**示例:**如果除法过程没有异常则输出结果

try:num1 = int(input('请输入被除数:'))num2 = int(input('请输入除数:'))result = num1 / num2
except Exception as error:print('出错了:', error)
else:print('计算成功,结果是:', result)    # 无异常时执行
9.2.3 强制执行:try-except-finally

finally子句无论try块是否发生异常,都会执行。常用于资源清理(如关闭文件、网络连接)。

语法规则:

try:可能出错的代码
except [异常类型 [as error]]:            # 将捕获到的异常对象赋值error捕获异常后的处理代码
finally:一定执行的代码

示例:

try:file = open('f:\\luckycloud.txt', mode='r', encoding='utf-8')print(file.read())
except FileNotFoundError as error:print('文件操作出错:', error)
finally:file.close()         # 无论是否报错,均关闭文件print('文件已关闭')

9.3:主动抛出异常:raise 与 assert

除了程序自动触发的异常,开发人员也可通过语句主动抛出异常,用于校验输入、标记业务错误等场景。

9.3.1 raise 语句

显式抛出指定异常,支持三种使用格式,可自定义异常描述。

raise语句的语法格式如下:

raise 异常类               # 格式1:使用异常类名引发指定的异常
raise 异常类对象           # 格式2:使用异常类的对象引发指定的异常
raise                    # 格式3: 使用刚出现过的异常重新引发异常
# 格式1:直接抛出异常类
raise IndexError# 格式2:通过异常对象抛出(可加描述)
index_error = IndexError('索引下标超出范围')
raise index_error# 格式3:重新抛出刚捕获的异常
try:5 / 0
except:print('捕获到异常,重新抛出')raise# 格式4:异常链(由一个异常引发另一个异常)
try:5 / 0
except Exception as e:raise IndexError('下标异常') from e  # 保留原始异常上下文
9.3.2 assert 语句(断言语句)

用于判断表达式是否为真,若为假则抛出AssertionError异常。适合调试阶段的逻辑校验(如参数合法性)。

语法格式:

assert 表达式[, 异常信息]

示例:

num1 = int(input('请输入被除数:'))
num2 = int(input('请输入除数:'))
assert num2 != 0, '除数不能为0'  # 表达式为False时触发异常
result = num1 / num2
print(f'{num1} / {num2} = {result}')

执行结果(若输入除数为 0):

Traceback (most recent call last):File "E:\pyproject\异常\assert.py", line 3, in <module>assert num2 != 0,'除数不能为0' #assert语句判定num2不等于0^^^^^^^^^
AssertionError: 除数不能为0
9.3.3 异常的传递机制

异常会沿函数调用自内向外传递,直到被捕获或导致程序终止。

示例:

def func_b():raise ValueError('输入值无效')  # 内层函数抛出异常def func_a():func_b()  # 调用内层函数,未捕获异常try:func_a()  # 外层捕获传递过来的异常
except ValueError as e:print('捕获到异常:', e)

输出:捕获到异常:输入值无效

9.4:自定义异常

当 Python 内置异常无法满足业务需求时,可自定义异常类。需继承Exception类(或其子类),类名建议以Error结尾,增强可读性。

自定义异常示例:

# 定义自定义异常类    
class ShortPwd(Exception):'''自定义异常'''      def __init__(self, length, atleast):self.length = lengthself.atleast = atleasttry:text = input('请输入密码:')if len(text) < 3:raise ShortPwd(len(text), 3)except ShortPwd as result:print('ShortPwd:输入的长度是%d,长度至少应该是%d' % (result.length, result.atleast))

运行结果:

请输入密码:12
ShortPwd:输入的长度是2,长度至少应该是3
请输入密码:1234
设置密码成功
9.4.1 自定义异常要点
  • 必须继承Exception(而非BaseException,避免捕获系统级异常)。
  • 可添加自定义属性和方法,扩展异常信息。
  • 配合raise语句使用,在业务逻辑不满足时主动触发。
http://www.dtcms.com/a/596527.html

相关文章:

  • MyBatis-Plus 进阶实战:不用写 XML,CRUD + 复杂查询像 “点奶茶” 一样简单
  • 野狗算法详细原理,野狗算法公式,野狗算法求解目标函数极值
  • V-Ray 渲染高效优化指南
  • 商丘网站建设的公司哪家好菏泽网站建设
  • RobotFramework框架基本功能语法
  • 120. 三角形最小路径和
  • Redis(116)Redis的内存管理如何实现?
  • 如何通过中药饮片采购平台实现安全高效的全链路供应链建立?
  • 兴县做网站的公司wordpress登陆改图标和连接
  • CSV的读写
  • 【讨论】VR + 具身智能 + 人形机器人:通往现实世界的智能接口
  • android studio 里的 activity 和 layout 是怎么关联上的
  • 临桂区建设局网站北京seo做排名
  • 东阳建设局网站自适应网站建设多少钱
  • UI创作思想总结
  • 4、Python3.11新特性
  • Java-Spring入门指南(二十九)Android交互核心:按钮点击事件与Activity跳转实战
  • 云手机的两大核心技术
  • Postman VS swagger VS PostIn,接口管理工具选型指南
  • 【大数据技术05】数据加工
  • es 书籍检索-上篇
  • Electron 智能文件分析器开发实战适配鸿蒙
  • 鸿蒙文件操作
  • 手工做环保衣的网站湖北省工程建设信息官方网站
  • ShellCrash 安装与配置踩坑与解决日志
  • redis 的面试点总结
  • DS1302 简单介绍
  • JS DOM 操作与性能优化实战指南:构建高效可交互的页面结构
  • 【Java】基于策略模式 + 工厂模式多设计模式下:重构租房系统核心之城市房源列表缓存与高性能筛选
  • 学校门户网站作用收录查询工具