当前位置:   article > 正文

全网最全《零基础学 Python》笔记

全网最全《零基础学 Python》笔记

阅读说明:

  1. 该笔记是本人《抖音:程序员头牌 | vx: cd20242024》经历三个月打磨编写的,其中参考阅读的书籍为《流程的python》。本笔记加入了工作中遇到的一些问题和对应的见解
  2. 有写得不对得、需要添加的知识点,可以在评论区指出,本人会对相应的进行修改和添加
  3. 有pdf版本,需要可以@我,记得一键三联哟!禁止转载!

文章目录

第1章 环境搭建

1.1 注意事项

学习必读:

  • 本教程主要针对大学学生准备的《零基础学 Python》 的学习教程

  • 课程适合的专业:大学所有专业均可学习,本课程考虑到了非计算机专业的学生,会补充一些非常基础的知识,如果你已知道该小节知识点,可以跳过该小节

  • 学习习惯:看一小节视频、写一小节的代码,养成复习的习惯,晚上睡觉前复习一下白天学习的知识点,多动手练习

1.2 本教程使用环境介绍

学习该课程,尽量严格按照老师的环境要求、搭建流程来搭建环境,减少学习途中遇到的各种环境问题

指标参数解释
电脑系统win11win10、win11 都是可以的
PythonPython 3.11Anaconda 控制版本
AnacondaAnaconda3-2023.07-1-Windows-x86_64下载连接:官方 | 国内加速

强烈安装的软件:

3.3浏览器:Google Chrome 网络浏览器

windows 解压工具WinRAR

文本编辑工具: Notepad ++

markdown 编辑器:typora

截图 + 贴图工具: snipaste

中国风的作图工具(谷歌浏览插件): Excalidraw

画流程工具: drawio

1.3 环境搭建

1.3.1 需要搭建的环境

  • Anaconda:

    是一个开源的 Python 发行版本,其包含了 conda、Python 等180多个科学包及其依赖项, conda 是一个开源的包、环境管理器,可以用于在同一个机器上安装不同版本的软件包及其依赖,并能够在不同的环境之间切换

    注意: 本教程使用 Anaconda 控制 Python 版本,不需要单独搭建 Python 环境,Anaconda 自带 Python

  • Pycharm:

    PyCharm 是一款 Python 的代码编辑器, 功能强大,学习/企业开发 Python 项目均使用此软件

1.3.2 注意事项

  1. 已经安装 Python 和 Pycharm, 再装 Anaconda 需不需要卸载原有的Python?

    根据老师使用经验和教学经验中遇到的问题来讲,一般不直接安装 Python, 使用 Anaconda 环境管理,自带一个 base环境,强烈推荐卸载已经安装的 Python,卸载干净以后,再安装 Anaconda,可以避免很多问题,而 Pycharm 不用重新安装!

    彻底卸载 Python 参考教程

  2. 已经安装 Anaconda了,但是版本和老师的不一样,需要卸载重新安装吗?

    如果 Anaconda 支持 Python 3.11 ,推荐不用重新安装,如果不支持 Python 3.11,说明版本太老了,则推荐卸载重新安装。因为本教程是基于 Python 3.11录制的,有新特性,低版本的无法使用新特性的功能

    彻底卸载 Anaconda 参考教程

1.4 安装 Anaconda

1.4.1 安装流程

双击 Anaconda3-2023.03-Windows-x86_64.exe 运行,跟着下面图文教程安装即可:

  1. 勾选 Just Me,表示 仅当前登录的用户使用,再点击 Next 下一步

  2. Anaconda 默认安装路径是 C 盘,由于 C 盘是系统盘,Anaconda 占的硬盘内存比较大,容易导致 C 盘的硬盘内存不足,因此修改默认安装路径,修改 Anaconda 安装路径为:D:\Python\anaconda3 ;修改安装路径后,点击 Next 下一步

  3. 勾选前三个选项。点击 Install ,之后等待安装

  4. 安装完成后,点击 Next 之后,Anaconda 安装就完成了!

在这里插入图片描述

1.4.2 配置国内镜像源

  • 因为 Anaconda 默认使用国外镜像,下载速度相对较慢,为了节省时间提高效率,需要将源设置修改为国内镜像源, 国内有很多源,推荐选择清华源,因为他已经被官方认可了!

  • 网上五花八门的配置教程,很多都过时了;正确方法就是参考 "清华大学开源软件镜像站"提供的教程

点击 Anaconda navigator 左上角的 File-> preferences , 有个弹窗出来,再点击

Configure Conda ,将下面配置复制粘贴到里面,最后点击保存即可

在这里插入图片描述

清华大学源

channels:
  - defaults
show_channel_urls: true
default_channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch-lts: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

运行 conda clean -i 清除索引缓存,保证用的是镜像站提供的索引。

由于 Anaconda 自带的 base 虚拟环境,下载了很多包,很多包目前学习中不会用到,同时包太多,每次新建项目都要更新索引,会非常慢,因此新建一个名叫:python-3-11、python 版本为 3.11 的虚拟环境,新建的环境包很少,比较适合学习,并设置为 Anaconda 默认虚拟环境,之后本教程均使用该环境!

上海交通大学

channels:
  - defaults
show_channel_urls: true
channel_alias: https://anaconda.mirrors.sjtug.sjtu.edu.cn/
default_channels:
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/main
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/free
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/mro
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/msys2
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/pro
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/r
custom_channels:
  conda-forge: https://anaconda.mirrors.sjtug.sjtu.edu.cn/conda-forge
  soumith: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/soumith
  bioconda: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/bioconda
  menpo: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/menpo
  viscid-hub: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/viscid-hub
  atztogo: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/atztogo
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

阿里云源

channels:
  - defaults
show_channel_urls: true
default_channels:
  - http://mirrors.aliyun.com/anaconda/pkgs/main
  - http://mirrors.aliyun.com/anaconda/pkgs/r
  - http://mirrors.aliyun.com/anaconda/pkgs/msys2
custom_channels:
  conda-forge: http://mirrors.aliyun.com/anaconda/cloud
  msys2: http://mirrors.aliyun.com/anaconda/cloud
  bioconda: http://mirrors.aliyun.com/anaconda/cloud
  menpo: http://mirrors.aliyun.com/anaconda/cloud
  pytorch: http://mirrors.aliyun.com/anaconda/cloud
  simpleitk: http://mirrors.aliyun.com/anaconda/cloud
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

北京外国语大学源

channels:
  - defaults
show_channel_urls: true
channel_alias: https://mirrors.bfsu.edu.cn/anaconda
default_channels:
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/main
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/free
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/r
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/pro
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.bfsu.edu.cn/anaconda/cloud
  msys2: https://mirrors.bfsu.edu.cn/anaconda/cloud
  bioconda: https://mirrors.bfsu.edu.cn/anaconda/cloud
  menpo: https://mirrors.bfsu.edu.cn/anaconda/cloud
  pytorch: https://mirrors.bfsu.edu.cn/anaconda/cloud
  simpleitk: https://mirrors.bfsu.edu.cn/anaconda/cloud
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

1.5 安装 Pycharm

1.5.1 安装流程

双击 pycharm-professional-2023.1.exe 运行,跟着下面图文教程安装即可:

  1. 点击 Next,下一步
  2. 修改 Pycharm 安装路径为:D:\Python\PyCharm 2023.1 ,再点击 Next,下一步
  3. 勾选所有选项,点击 Next,下一步
  4. 点击 Install ,之后等待安装
  5. 安装完成后,点击 Finish ,安装就完成了

在这里插入图片描述

1.5.2 pycharm 全局配置

  • 破解激活
  • 软件汉化
  • 设置全局 utf8 编码
  • 详细流程,请观看视频教程

至此,所有软件安装完成了!

第2章 基础语法

2.1 Python 语法特点

2.1.1 注释

注释就是对代码的解释和说明,让人了解代码实现的具体功能,从而帮助程序员更好地阅读代码,在 Python 中,包含三种类型的注释

  1. 单行注释

    # 这是注释内容
    
    • 1
  2. 多行注释

    '''
    这是单引号注释内容
    '''
    
    """
    这是双引号注释内容
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  3. 文件编码声明注释

    1945年,美国发明了世界上第一台计算机,同时起草了计算机的第一份字符集和编码标准,叫 ASCII(类似汉语词典),一共规定了 128 个字符及对应的二进制转换关系,128 个字符包括了26个字母(大小写)、10个数字、标点符号以及特殊的控制符,也就是英语与西欧语言中常见的字符.

    在这里插入图片描述

    随着计算机的不断普及,计算机开始被西欧等国家使用,然而西欧语言中还有很多字符不在 ASCII 字符集中,这给他们使用计算机造成了很大的限制。于是乎,他们想着法子把 ASCII 字符集进行扩充,这就是后来的 EASCII,然后 EASCII 并没有形成统一的标准,各国商家都有自己的小算盘,用了各自定义的编码字符集。为了结束这种混乱的局面,国际标准化组织(ISO)和国际电工委员会(IEC)联合制定的一系列字符集的标准,叫 ISO 8859。

    后来,计算机开始普及到了中国,但面临的一个问题就是字符,汉字博大精深,常用汉字有3500个,已经大大超出了 ASCII 字符集所能表示的字符范围了,即使是 EASCII 也显得杯水车薪,于是在 1981 年,由国家带头,制定了一套专属于中国的字符集叫 GB2312,理论上它可以表示65536个字符,不过它只收录了7445个字符,同时它能够兼容 ASCII。GB2312 所收录的汉字已经覆盖中国大陆99.75%的使用频率,但是对一些罕见的字和繁体字还有很多少数民族使用的字符都没法处理,于是后来就 GB2312 的基础上创建了一种叫 GBK 的字符编码,GBK 不仅收录了27484 个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。

    尽管我们有了属于自己的字符集和字符编码 GBK,可世界上还有很多国家和地区拥有自己的语言和文字,比如日本用 JIS,中国台湾(使用繁体)用 BIG5,不同国家之间交流起来就很困难,因为没有统一的编码标准,可能同一个字符,在A国家用两字字节存储,而到了B国家是3个字节,这样很容易出现编码问题,于是在 1991 年,国际标准化组织和统一码联盟组织各自开发了 ISO/IEC 10646(USC)和 Unicode 项目,这两个项目的目的都是希望用一种字符集来统一全世界所有字符,不过很快双方都意识到世界上并不需要两个不兼容的字符集。于是他们就编码问题进行了非常友好地会晤,决定彼此把工作内容合并,虽然项目还是独立存在,各自发布各自的标准,但前提是两者必须保持兼容。不过由于 Unicode 这一名字比较好记,因而它使用更为广泛,成为了事实上的统一编码标准。

    Unicode 是一个囊括了世界上所有字符的字符集,其中每一个字符都对应有唯一的编码值,注意了!它不是字符编码,仅仅是字符集而已,Unicode 字符如何进行编码,可以是 UTF-8、UTF-16、甚至用 GBK 来编码。

    UTF-8 编码在互联网领域应用更加广泛,Python 3+ 使用 UTF-8

    注意

    我们一般说的 UTF-8 编码指的是 字符集使用 Unicode,编码方式使用 UTF-8 进行编码

    Python 文件编码声明注释

    语法格式

    # *-* coding:utf8 *-*
    
    • 1

    或者

    # coding:utf8
    
    • 1

    技巧:可以在 Pycharm 中全局设置编码方式为 utf-8

2.1.2 代码缩进

Python 不像其他编程语言,如 CJava 采用大括号 {} 分隔代码块,而是采用冒号 : 和代码缩进 区分代码之间的层次

缩进可以使用空格或者<Tab> 键实现,其中,使用空格时,通常采用4个空格作为一个缩进单位,使用 <Tab> 键时,采用一个 <Tab> 键作为一个缩进单位,在 Pycharm 中通常建议采用 <Tab> 键进行缩进

# 定义年龄
age = 17
if age < 18:
    print("未成年")
  • 1
  • 2
  • 3
  • 4

2.1.3 代码规范

Python 中采用 PEP 8 作为编码规范,Pycharm 软件会自动对代码进行 PEP 8 编码校验,如果不符合代码规范的,代码下边会有波浪线提示!常见规则如下:

PycharmPEP 8 规范格式化快捷键:Ctrl + alt + L

  • 不要在代码结尾添加 ;

  • 建议每行代码不要超过 80 个字符,如果超过,建议使用小括号 () 连接起来

  • 使用必要的空行可以增加代码的可读性

  • 通过情况下,运算符两侧、函数参数之间,, 两侧,建议使用空格进行隔离

  • 模块名尽量短小,并且全部使用小写字母,可以使用下划线分隔多个字母

  • 包名尽量短小,并且全部使用小写字母,包之间强烈推荐使用 "." 连接 不推荐使用下划线,列如:com.baidu.Python

  • 类名采用大驼峰命名,每个单词首字母大写,其他字母小写

  • 模块内部的类采用下划线"_" + 类名

  • 函数、类的属性和方法的命名规则同模块的类似,也是全部小写字母,多个字母间用下划线_分隔

  • 常量命名时采用全部大写字母,可以使用下划线

  • 使用单下划线_开头的模块变量或者函数是受保护的,在使用from xxx import * 语句从模块中导入时这些变量或函数不能被导入

  • 使用双下划线__开头的实例变量或方法是类私有的

2.2 保留字与标识符

2.2.1 保留字

保留字(关键字)是 Python 语言中一些已经被赋予特定意义的单词,开发程序时,不可以把这些保留字作为变量、函数、类、模板和其他对象的名称来使用。常见的保留字表如下:

andasassertbreakclasscontinue
defdelelifelseexceptfinally
forfromFalseglobalifimport
inislambdanonlocalnotNone
orpassraisereturntryTrue
whilewithyield

Python 中所有保留字是区分字母大小写的,列如 and 是保留字,但是 And 就不属于保留字, 其中 Pycharm 软件中会自动检测标识符是否是保留字,是保留字,则编译不过

2.2.2 标识符

标识符可以简单的理解为一个名字,比如每个人都有自己的名字,它主要是来标识变量名、函数命、类名、模块和其他对象的名称。

Python 语言标识符命名规则如下:

  • 字母(a-z,A_Z)数字下划线 "_" 组成,第一个字符不能是数字

  • 不能使用 Python 保留字(关键字)

  • 严格区分字母大小写

案列:

合法的标识符

USER_ID # 大写字母 + 下划线
username # 全小写字母
User_age123 # 大写字母 + 小写字母 + 下划线 + 数字
user123 # 小写字母 + 数字
_age # 下划线 + 小写字母
__name # 下划线 + 小写字母
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

不合法的标识符

4user    # 第一个字符不能是数字
and      # and 为 Python 中的保留字
$user    # 不能使用特殊符号,符号只能使用下划线 "_"
  • 1
  • 2
  • 3

严格区分大小写

# age 和 Age 是两个不同的标识符
age
Age
  • 1
  • 2
  • 3

2.3 变量

2.3.1 变量的理解

变量严格意义上应该称为 名字,你的快递存放在货物架上,上面贴着写有你名字的标签。当你来取快递时,并不需要知道它们存放在这个大型货架的具体位置,只需要提供你的名字,快递员就会把你的快递交给你。实际上,变量也一样,你不需要知道信息存储在内存中的准确位置,只需要记住存储变量时所用的名字,再调用这个名字就可以了

2.3.2 变量的定义与使用

变量名本质就是一个有效的标识符。

使用变量的一些规则和经验:

  • 变量名必须是一个有效的标识符

  • 变量名不能使用 Python 中的保留字(关键字)

  • 应该选择有意义的单词作为变量名,比如年龄 age

  • 慎用小写字母l和大写字母O,因为小写字母l和数字1肉眼看起来特别相似,同样大写字母O和数字0肉眼看起来特别相似,记住这条经验可以减少开发中遇到该项问题

为变量赋值,可以通过等号 "=" 来实现,语法格式为:

变量名 = 变量值
  • 1

案列

# 数字1024:创建变量名为 number,并赋值为 1024,该变量类型为 整数类型
number = 1024
# 姓名 张三:创建变量名为 username,并赋值为 张三,该变量类型为 字符串类型
username = "张三"
# 使用 内置函数 type() 可返回变量类型
# 输出: number 变量 类型为: <class 'int'>
print("number 变量 类型为:", type(number))
# 输出: username 变量类型为: <class 'str'>
print("username 变量类型为:", type(username))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Python 是一种动态类型的语言,也就说,变量的类型可以随时变化

# 创建变量名为 username,并赋值为 张三,该变量类型为 字符串类型
username = "张三"
# 输出: username 变量 类型为: <class 'str'>
print("username 变量 类型为:", type(username))
username = 1024
# 输出: username 变量类型为: <class 'int'>
print("username 变量类型为:", type(username))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用 内置函数 type() 可返回变量类型

2.4 基本数据类型

在这里插入图片描述

2.4.1 数值类型

Python 语言中,数字类型主要包含:整数、浮点数、复数、布尔

2.4.1.1 整数

数学中,整数包括正整数负整数0,案列:

number1 = 1
print(number1)  # 1

number2 = -35
print(number2)  # -35

number3 = 0
print(number3)  # 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
2.4.1.2 浮点数

浮点数(对小数的近似表示)由整数部分和小数部分组成,列如:3.14159260.5-1.65 等等,浮点数也可以是科学计数法标识,列如:2.7e2(2.7 x 10的2次方),-5e5(-5 x 10 的5次方)等等

float1 = 3.1415926
print(float1)  # 3.1415926

float2 = 0.5
print(float2)  # 0.5

float3 = -1.65
print(float3)  # -1.65

# 2.7 x 10的2次方
float4 = 2.7e2
print(float4)  # 270.0

# -5 x 10 的5次方
float5 = -5e5
print(float5)  # -500000.0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
2.4.1.3 复数

由于工作中很少用到,这里就不讲解了

2.4.2 布尔类型

布尔类型主要用于表示真值或假值, 在 Python中 标识符 TrueFalse 被解释为布尔值,另外布尔值可以转化为数值,即True代表整数1False代表整数0

Python中 只有如下几种情况得到的值为假

  1. FalseNone

  2. 数值中的零:包括 00.0

  3. 空序列:包括 空字符("")空列表([])空字典({})空元组(())

总结:非空即真,非零即真 这里先不给大家讲解,讲到流程控制语句再详细讲解

案列

print(True)  # True
print(False)  # False
print(type(True))  # <class 'bool'>
print(type(False))  # <class 'bool'>
print(3 > 1)  # True
print(3 > 5)  # False
print(True + 1)  # 1 + 1 = 2
print(False - 1)  # 0 - 1  = -1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.4.3 字符串类型

字符串就是连续的字符序列,在 Python 中,通常使用单引号''双引号""三引号'''或"""表示

案列

# 单引号
name1 = '张三'
print(name1)  # 张三

# 双引号
name2 = "张三"
print(name2)  # 张三

# 三单引号
name3 = '''
我是张三,今年18岁,我
今年刚考入大学读书
'''
print(name3)

# 三双引号
name4 = """
我是张三,今年18岁,我
今年刚考入大学读书
"""
print(name4)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Python 3.6 引入了一种新的字符串格式化方式:f-string 格式化字符串, 这种格式化的方式越来越直观,以后有且仅推荐使用该方式

x = 2
y = 3

# 在字符串最前面 输入小写字母
a = f"x = {x}, y = {y}"
print(a)  # x = 2, y = 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

后面讲序列化的时候,再详细讲解其他功能

2.4.4 数据类型转换

虽然 Python 是弱类型编程语言,不需要像 JavaC 语言那样还要在使用变量前声明变量的类型,但在一些特定场景中,仍然需要用到类型转换。下表是 Python 中常用数据类型转换函数,只需要大家掌握 int(x)float(x)str(x)chr(x) 这 4 个即可

函 数作 用
int(x)将 x 转换成整数类型
float(x)将 x 转换成浮点数类型
str(x)将 x 转换为字符串
chr(x)将整数 x 转换为一个字符

案列

# 浮点数 2.2 转换成整数类型
num1 = int(2.4)
print(num1)  # 2

# 整数 2 转换成浮点数类型
num2 = float(2)
print(num2)  # 2.0

# 浮点数 3.14 转换为字符串 3.14
num3 = str(3.14)
print(type(num3))  # <class 'str'>
print(num3)  # 3.14

# 整数 97 对应 asccll 编码表中的小写字母 a
num4 = chr(97)
print(num4)  # a
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.5 运算符

2.5.1 算术运算符

算术运算符就是数学运算符,比如加减乘除

下表列出了 Python 支持常见的基本算术运算符

运算符说明实例结果
+加法10.5 + 1020.5
-减法4.5 - 0.254.25
*乘法4 * 2.510.0
/除法5 / 22.5
//整除:只保留商的整数部分5 // 22
%取余:返回除法的余数5 % 21
**幂运算:返回 x 的 y 次方2 ** 3(2的4次方)8
# 加法
print(10.5 + 10)  # 20.5

# 减法
print(4.5 - 0.25)  # 4.25

# 乘法
print(4 * 2.5)  # 10.0

# 除法
print(5 / 2)  # 2.5

# 整除:只保留商的整数部分
print(5 // 2)  # 2

# 取余:返回除法的余数
print(5 % 2)  # 1

# 幂运算
print(2 ** 3)  # 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

某学员的课程成绩如下:

课程分数
C 语言89
Python84
mysql91

求三门课程的平均分?

# 定义变量,存储 c 语言课程的分数
c = 89
# 定义变量,存储 Python课程的分数
Python = 84
# 定义变量,存储 mysql 课程的分数
mysql = 93
# 求平均成绩
avg = (c + Python + mysql) / 3
# 三门课程的平均分为:88.66666666666667分
print(f"三门课程的平均分为:{avg}分")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.5.2 赋值运算符

赋值运算符用来把右侧的值传递给左侧的变量,可以直接将右侧的值交给左侧的变量,也可以右侧进行某些运算后再交给左侧的变量,比如加减乘除、函数调用、逻辑运算等。

运算符说 明用法举例等价形式
=赋值运算x = yx = y
+=加赋值x += yx = x + y
-=减赋值x -= yx = x - y
*=乘赋值x *= yx = x * y
/=除赋值x /= yx = x / y
%=取余数赋值x %= yx = x % y
**=幂赋值x **= yx = x ** y
//=取整数赋值x //= yx = x // y
x = 2
y = 3

x += y  # x = x + y  ->  x = 2 + 3 =5
print(f"x = {x}, y = {y}")  # 此时 x = 5, y = 3

x -= y  # x = x - y  ->  x = 5 - 3 = 2
print(f"x = {x}, y = {y}")  # 此时 x = 2, y = 3

x *= y  # x = x * y  ->  x = 2 * 3 = 6
print(f"x = {x}, y = {y}")  # x = 6, y = 3

x /= y  # x = x / y  -> x = 6 / 3 = 2.0
print(f"x = {x}, y = {y}")  # 此时 x = 2.0, y = 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.3.3 关系运算符(比较运算)

关系运算符,也称比较运算符,用于对常量、变量或表达式的结果进行大小比较。如果比较是成立的,则返回 True(真),反之则返回 False(假)

说明
>大于,如果>前面的值大于后面的值,则返回 True,否则返回 False
<小于,如果<前面的值小于后面的值,则返回 True,否则返回 False
==等于,如果==两边的值相等,则返回 True,否则返回 False
>=大于等于(等价于数学中的 ≥),如果>=前面的值大于或者等于后面的值,则返回 True,否则返回 False
<=小于等于(等价于数学中的 ≤),如果<=前面的值小于或者等于后面的值,则返回 True,否则返回 False
!=不等于(等价于数学中的 ≠),如果!=两边的值不相等,则返回 True,否则返回 False
is判断两个变量所引用的对象是否相同,如果相同则返回 True,否则返回 False
is not判断两个变量所引用的对象是否不相同,如果不相同则返回 True,否则返回 False
print(f"90是否大于100:{90 > 100}")  # 90是否大于100: False
print(f"25x4是否大于等于99:{25 * 4 >= 99}")  # 25x4是否大于等于99: True
print(f"10是否等于10:{10 == 10}")  # 10是否等于10: True
print(f"2.0是否等于2:{2.0 == 2}")  # 2.0是否等于2: True
print(f"False是否小于True:{False < True}")  # 0 < 1: False是否小于True: True
print(f"True是否等于True:{True < True}")  # 1 < 1 : True是否等于True: False
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.5.4 逻辑运算符

数学中我们就学过逻辑运算,例如 p 为真命题,q 为假命题,那么p且q为假,p或q为真,非q为真等等

逻辑运算符含义基本格式说明
and逻辑 “与” 运算,等价于数学中的“且”a and bab 两个表达式都为真时,a and b 的结果才为真,否则为假。
or逻辑 “或” 运算,等价于数学中的“或”a or bab两个表达式都为假时,a or b 的结果才是假,否则为真。
not逻辑 “非” 运算,等价于数学中的“非”not a如果 a 为真,那么 not a 的结果为假;如果 a 为假,那么 not a 的结果为真。相当于对 a 取反。

短路运算

  • 对于 and 运算符

    • 如果左边表达式的值为假,那么就不用计算右边表达式的值了,因为不管右边表达式的值是什么,都不会影响最终结果,最终结果都是假,此时 and 会把左边表达式的值作为最终结果

    • 如果左边表达式的值为真,那么最终值是不能确定的,and 会继续计算右边表达式的值,并将右边表达式的值作为最终结果

  • 对于 or 运算符

    • 如果左边表达式的值为真,那么就不用计算右边表达式的值了,因为不管右边表达式的值是什么,都不会影响最终结果,最终结果都是真,此时 or 会把左边表达式的值作为最终结果

    • 如果左边表达式的值为假,那么最终值是不能确定的,or 会继续计算右边表达式的值,并将右边表达式的值作为最终结果

    age = "18"
    print(type(age))  # <class 'str'>
    
    print("---- False and age-----")
    # 如果左边表达式的值为假,那么就不用计算右边表达式的值了,因为不管右边表达式的值是什么,都不会影响最终结果,最终结果都是假,此时 and 会把左边表达式的值作为最终结果
    print(False and print(age))  # False
    
    print("----True and age-----")
    # 如果左边表达式的值为真,那么最终值是不能确定的,and 会继续计算右边表达式的值,并将右边表达式的值作为最终结果
    print(True and print(age))  # 18 、None
    
    print("----True or age-----")
    # 如果左边表达式的值为真,那么就不用计算右边表达式的值了,因为不管右边表达式的值是什么,都不会影响最终结果,最终结果都是真,此时 or 会把左边表达式的值作为最终结果
    print(True or print(age))  # True
    
    # 如果左边表达式的值为假,那么最终值是不能确定的,or 会继续计算右边表达式的值,并将右边表达式的值作为最终结果
    print("----False or age-----")
    print(False or print(age))  # 18 、None
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

2.5.5 位运算

Python 位运算按照数据在内存中的二进制位进行操作,它一般用于底层开发,在应用层开发中并不常见。同时涉及到的知识点比较多,我们先跳过本小节,以后需要的话再来学习

2.5.6 运算符的优先级

所谓优先级,就是当多个运算符同时出现在一个表达式中时,先执行哪个运算符,与数学的四则运算 先乘除,后加减,有括号时要先算括号内的 是一个道理

Python中运算符的运算规则是:优先级高的运算符先执行,优先级低的运算符后执行,同一优先级的操作按照从左到右的顺序进行

常用优先级运算规则表如下:

小技巧

在编写程序的时候,尽量使用括号 "()" 来限定运算符的次序,避免运算符次序发生错误

运算符说明Python运算符优先级
小括号( )19
索引运算符x[i] 或 x[i1: i2 [:i3]]18
属性访问x.attribute17
乘方**16
按位取反~15
符号运算符+(正号)、-(负号)14
乘除*///%13
加减+-12
位移>>、<<11
按位与&10
按位异或^9
按位或|8
比较运算符==!=>>=<<=7
is 运算符is、is not6
in 运算符in、not in5
逻辑非not4
逻辑与and3
逻辑或or2
逗号运算符exp1, exp21

2.6 基本输入输出

2.6.1 常用输入

input()Python 的内置函数,用于从控制台读取用户输入的内容。input() 函数总是以字符串的形式来处理用户输入的内容,所以用户输入的内容可以包含任何字符,其中注意按下回车键后 input() 读取就结束了

input() 函数的用法为:

str = input(tipmsg)
  • 1

说明:

  • str 表示一个字符串类型的变量,input 会将读取到的字符串放入 str 中。
  • tipmsg 表示提示信息,它会显示在控制台上,告诉用户应该输入什么样的内容;如果不写 tipmsg,就不会有任何提示信息

实战:

题目:请在控制台输入你的PythonC语言、MySql等三门课程成绩,并打印三门课程成绩和平均成绩

提示:float() 函数将字符串数字转换成浮点型数字

"""
题目:请在控制台输入你的Python、C语言、MySql等三门课程成绩,并打印三门课程成绩和平均成绩
提示:float() 函数将字符串数字转换成浮点型数字
"""
Python = float(input("请输入你的 Python 的课程成绩 (必须位数字):"))
c = float(input("请输入你的 C 语言的课程成绩 (必须位数字):"))
mysql = float(input("请输入你的 MySql 的课程成绩 (必须位数字):"))
print(f"Python 的课程成绩为:{Python}")
print(f"C 语言的课程成绩:{c}")
print(f"MySql 的课程成绩为:{mysql}")

# 计算平均成绩
avg = (Python + c + mysql) / 3
print(f"平均成绩为:{avg}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.6.2 常用输出

前面使用 print() 函数时,都只输出了一个变量,但实际上 print() 函数完全可以同时输出多个变量,而且它具有更多丰富的功能

第3章 流程控制语句

3.1 程序结构

和其它编程语言一样,按照执行流程划分,Python 程序也可分为 3 大结构,即顺序结构选择结构循环结构

  • 顺序结构: 程序从上到下逐行地执行

  • 选择结构: 根据条件,选择性地执行某段代码

  • 循环结构: 根据循环条件,重复性的执行某段代码

    在这里插入图片描述

3.2 选择语句

3.2.1 if 语句

# 语法格式
if 表达式:
    语句块
  • 1
  • 2
  • 3

如果表达式为,则执行 语句块;表达式如果为,就跳过 语句块 ,继续执行后面的语句,这种形式的if语句,相当于汉语里的关联词语 如果......, 就......

在这里插入图片描述

需求:控制台输入你的年龄,如果该同志的年龄小于 18 岁,则输出“青少年”

age = int(input("请输入年你的年龄:"))
if age < 18:
    print("青少年")
  • 1
  • 2
  • 3

3.2.2 if…else 语句

# 语法格式
if 表达式:
    语句块1
else:
    语句块2
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

需求:控制台输入你的年龄,如果该同志的年龄小于 18 岁,则输出“青少年”,否则输出 “已成年”

age = int(input("请输入年你的年龄:"))
if age < 18:
    print("青少年")
else:
    print("已成年")
  • 1
  • 2
  • 3
  • 4
  • 5

3.2.3 if…elif…else 语句

# 语法格式
if 表达式1:
    语句块1
elif 表达式2:
    语句块2
elif 表达式3:
    语句块3
......
......
......
else:
  语句块n
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里插入图片描述

需求:编写程序根据分数打印结果,90分及以上为优秀,80分及以上为良好,60分及以上为及格,60分以下为不及格

# 定义分数为85分
score = 85
if score >= 90:
    print("优秀")
elif score >= 80:
    print("良好")
elif score >= 60:
    print("及格")
else:
    print("不及格")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.2.4 if 语句的嵌套

if语句中嵌套if...else语句

if 表达式1:
    if 表达式2:
        语句块1
    else:
        语句块2
  • 1
  • 2
  • 3
  • 4
  • 5

if...else语句中嵌套if...else语句

if 表达式1:
    if 表达式2:
        语句块1
    else:
        语句块2
else:
    if 表达式3:
        语句块3
    else:
        语句块4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

需求:判断一个年份是不是闰年

我们先搞清楚什么是闰年

  1. 第一种,是普通闰年,能被4整除但不能被100整除(如2008年就是普通闰年,而1900年不是)

  2. 第二种,比较少见的,世纪闰年:能被400(4x100)整除(如2000年是世纪闰年,1900年不是世纪闰年)

year = int(input('请输入一个年份:'))
# 不管是普通闰年,还是世纪闰年 都能被4整除
if year % 4 == 0:
    if year % 100 == 0:
        if year % 400 == 0:
            # 能被400整除
            print(f"{year} 是闰年!")
        else:
            print(f"{year} 不是闰年!")
    else:
        # 能被4整除但不能被100整除
        print(f"{year} 是闰年!")
else:
    print(f"{year} 不是闰年!")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

很明显这种方式代码可读性非常差

使用如下这种方式更容易理解

year = int(input("请输入年份:"))
if year % 4 == 0 and year % 100 != 0:
    print(f"{year} 是闰年!")
elif year % 400 == 0:
    print(f"{year} 是闰年!")
else:
    print(f"{year} 不是闰年!")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

或者

year = int(input("请输入年份:"))
if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
    print(f"{year} 是闰年!")
else:
    print(f"{year} 不是闰年!")
  • 1
  • 2
  • 3
  • 4
  • 5

3.3 条件表达式

# 语法格式
表达式1 if 条件 else 表达式2
  • 1
  • 2

如果 条件真,就执行 表达式1,并把 表达式1 的结果作为整个表达式的结果;

如果 条件假,就执行 表达式2,并把 表达式2 的结果作为整个表达式的结果

先看下面这个代码

# 将 a b 中最大的值 赋值给 maximum
a = 2
b = 3
maximum = 0
if a > b:
    maximum = a
else:
    maximum = b
print(f" maximum = {maximum}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

针对上面的代码,可以使用条件表达式进行简化,代码如下所示:

 # 将 a b 中最大的值 赋值给 maximum
a = 2
b = 3
maximum = a if a > b else b
print(f" maximum = {maximum}")
  • 1
  • 2
  • 3
  • 4
  • 5

3.4 循环语句

3.4.1 while 循环

①初始化部分
while ②循环条件:
    ③循环体
  • 1
  • 2
  • 3

当循环条件为真时,则执行循环体,当循环条件为假时,退出循环。

执行过程:① - ② - ③ - ② - ③ - ② - ③ - … - ②

  • while 循环千万要小心不要丢了迭代条件。一旦丢了,就可能导致死循环!

在这里插入图片描述

需求:求1+2+3+4+ … + 100之和,并打印输出

# 初始求和变量为0
sum = 0
# 初始化条件
i = 1
# 循环条件
while i <= 100:
    # 循环体
    sum += i  # sum = sum + i
    # 迭代条件
    i += 1  # i = i + 1
# 打印输出结果
print(sum)  # 5050
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.4.2 for 循环

# 语法格式
for 迭代变量 in 序列:
    循环体
  • 1
  • 2
  • 3

其中,迭代变量用于保存读取出的值,序列可以是字符串、列表、元组、集合、字典等等

在这里插入图片描述

需求:逐个遍历打印字符串 hello word 中的每个字符

name = "hello word"
for item in name:
    # 打印每个字符
    print(item)
  • 1
  • 2
  • 3
  • 4

尽管除字符串以外,其他的序列类型目前没有学习到,但是我们不妨碍我们学习 range 语句,获得一个简单的数字序列

语法格式: range(start,end,step)

参数说明:

  • start: 用于指定计数的起始值,可以忽略,如果省略则默认从0开始

  • end: 用于指定计数的结束值(但是不包含该值,如果 range(3), 则得到的值为:[0,1,2], 不包括 7),不能省略,当 range() 函数中只有一个参数时,既表示指定计数的结束值.

  • step: 用于指定步长,既两个数之间的间隔,可以省略,如果省略则表示步长为 1,如:range(1,7) 将得到 [1,2,3,4,5,6]

  • range(1,10,2) 表示: 生成从1开始,到10结束(不包含10),步长为2的序列,既:[1,3,5,7,9]

需求:需求:求1+2+3+4+ ..... + 100之和,并打印输出

# 初始求和变量为0
sum = 0
# 生成 起始值为1,结束之为101(不包含101),步长为1的 数字序列:[1,2,3,4,5,...,100]
for item in range(1, 101, 1):
    sum += item
print(sum)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3.4.3 循环嵌套

Python 中,for 循环和 while 循环都可以进行嵌套

while 循环中嵌套 while 循环的格式

while 条件表达式1:
    while 条件表达式2:
        循环体2
    循环体1
  • 1
  • 2
  • 3
  • 4

for 循环中嵌套 for 循环的格式

for 迭代变量1 in 序列1:
    for 迭代变量2 in 序列2:
        循环体2
    循环体1
  • 1
  • 2
  • 3
  • 4

while 循环中嵌套 for 循环的格式

while 条件表达式:
    for 迭代变量 in 序列:
        循环体2
    循环体1
  • 1
  • 2
  • 3
  • 4

for 循环中嵌套 while 循环的格式

for 迭代变量 in 序列:
    while 条件表达式:
        循环体2
    循环体1
  • 1
  • 2
  • 3
  • 4

for 循环中嵌套 for 循环用得比较多,其他用得少

需求:使用 for 循环嵌套,打印九九乘法表

1 x 1 = 1    
1 x 2 = 2    2 x 2 = 4    
1 x 3 = 3    2 x 3 = 6    3 x 3 = 9    
1 x 4 = 4    2 x 4 = 8    3 x 4 = 12    4 x 4 = 16    
1 x 5 = 5    2 x 5 = 10    3 x 5 = 15    4 x 5 = 20    5 x 5 = 25    
1 x 6 = 6    2 x 6 = 12    3 x 6 = 18    4 x 6 = 24    5 x 6 = 30    6 x 6 = 36    
1 x 7 = 7    2 x 7 = 14    3 x 7 = 21    4 x 7 = 28    5 x 7 = 35    6 x 7 = 42    7 x 7 = 49    
1 x 8 = 8    2 x 8 = 16    3 x 8 = 24    4 x 8 = 32    5 x 8 = 40    6 x 8 = 48    7 x 8 = 56    8 x 8 = 64    
1 x 9 = 9    2 x 9 = 18    3 x 9 = 27    4 x 9 = 36    5 x 9 = 45    6 x 9 = 54    7 x 9 = 63    8 x 9 = 72    9 x 9 = 81    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

写代码之前,先补讲以下 print() 函数的其他用法

# print() 函数默认拼接符号是 换行符
print("111")
print("222")
# 输出内容为:
"""
111
222
"""

print("111", end="")
print("222", end="")
# 输出内容为:111222
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
print("打印九九乘法表")
for i in range(1, 10):  # i 表示第几行
    for j in range(1, i + 1):  # j 表示第几列
        # print() 函数默认拼接符号是换行符,这里设置拼接符为:""
        # 在Python中,“\t”是指制表符,代表着四个空格,也就是一个tab;它的作用是对齐表格数据的各列,可以在不使用表格的情况下,将数据上下对齐
        print(f"{j} x {i} = {i * j}\t", end="")
    print("")  # 换行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.5 break 和 continue 语句

Pythonbreakcontinue 他们的作用都是用来结束循环

  • break:for 循环或 while 循环结构中使用 break 语句,用于结束整个循环

  • continue:for 循环或 while循环结构中使用 continue 语句,用于结束当前循环

举例: 一共吃5个草莓

  • 情况一:如果在吃的过程中,吃完第三个就不想吃了,则不需要再吃第四个和第五个草莓了,即吃草莓的动作停止,这里使用 break 终止此程序

  • 情况二:如果在吃的过程中,吃到第三个,吃出一个虫子,这个草莓就不吃了,开始吃第四个草莓,这里使用 continue 退出当前一次循环继而执行下一次循环代码

需求:一共吃5个草莓,如果在吃的过程中,吃完第三个就不想吃了,则不需要再吃第四个和第五个草莓了,即吃草莓的动作停止,这里使用 break 终止此程序

# 生成 初始值为1,结束值为6(不包含6),步伐为1的 数字序列,既:[1,2,3,4,5]
for i in range(1, 6, 1):
    # 条件: 吃完第三个就不想吃了
    if i == 3:
        print('吃饱了,不吃了')
        break
    print(f'吃了{i}个苹果')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

输出:

吃了1个苹果
吃了2个苹果
吃饱了,不吃了

需求:一共吃5个草莓, 如果在吃的过程中,吃到第三个,吃出一个虫子,这个草莓就不吃了,开始吃第四个草莓,这里使用 continue 退出当前一次循环继而执行下一次循环代码

# 生成 初始值为1,结束值为6(不包含6),步伐为1的 数字序列,既:[1,2,3,4,5]
for i in range(1, 6, 1):
    # 条件: 吃完第三个就不想吃了
    if i == 3:
        print('吃出一个虫子,这个草莓不吃了')
        continue
    print(f'吃了{i}个苹果')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

输出:

吃了1个苹果
吃了2个苹果
吃出一个虫子,这个草莓不吃了
吃了4个苹果
吃了5个苹果

3.6 pass 空语句

在实际开发中,有时候我们会先搭建起程序的整体逻辑结构,但是暂时不去实现某些细节,而是在这些地方加一些注释,方面以后再添加代码,请看下面的例子:

age = int( input("请输入你的年龄:") )
if age < 18 :
    print("青少年")
elif 18 <= age < 60:
    # 成年人
else:
    print("老年人")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

当年龄大于等于 18 并且小于 60 时,我们没有使用 print()语句,而是使用了一个注释,希望以后再处理成年人的情况, 此时就可以通过 pass 语句来实现, 使用 pass 语句比使用注释更加优雅。

修改后的代码如下所示

age = int(input("请输入你的年龄:"))
if age < 18:
    print("青少年")
elif 18 <= age < 60:
    # 成年人
    pass
else:
    print("老年人")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.7 match…case 语句

match...case 语句又叫模式匹配,Python 3.10 新出的功能,match...case 语法类似于其他面向对象语言中的 switch...case 语句

语法:

match subject:
    case <匹配1>:
        代码1
    case <匹配2>:
        代码2
    case <匹配3>:
        代码3
    case _:
        代码4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 从上到下对 subjectcase 语句中的每个模式进行比较直到确认匹配到一个模式

  • 如果没有确认到一个完全的匹配,则如果提供了使用通配符 _ 的最后一个case 语句,则它将被用作已匹配模式。 如果没有确认到一个完全的匹配并且不存在使用通配符的 case 语句,则整个 match 代码块不执行任何操作

  • subject可以是数字、字符串、列表、元组、字典、集合、类、枚举

  • 模式匹配常用在数字、字符串、枚举这三个中

案列:匹配一个字面值

status = 500
if status == 400:
    print("这是400")
elif status == 404:
    print("这是404")
elif status == 500:
    print("这是500")
else:
    print("没有匹配到")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
def match_status(status):
    match status:
        case 400:
            print("这是400")
        case 404:
            print("这是404")
        case 500:
            print("这是500")
        case _:
            print("没有匹配到")


match_status(400)
match_status(404)
match_status(500)
match_status(600)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

第4章 容器的应用

Python语言中基本容器类型的接口如图,基本容器类型的UML类图,以蓝色字体显示的方法名表示抽象方法,必须由具体子类是实现,其他方法有具体实现,子类可以直接继承,

下图是collections.abc模块中的抽象基类图。

在这里插入图片描述

Iterable、Container和Sized

每个容器都应该继承这三个抽象基类,或者实现兼容的协议。

  • Iterable 通过__iter__()方法支持迭代

  • Container 通过__contains__()方法支持 in 运算符

  • Sized 通过__len__()方法支持 len() 函数

Collection

这个抽象基类是3.6新增的,自身没有方法,目的是方便子类化IterableContainerSized

Sequence、Mapping和Set

这3个抽象基类是主要的不可变容器类型,而且各自都有可变的子类

Iterator

注意它是Iterable的子类

Callable和Hashable

这两个不是容器,只不过因为Collections.abc是标准库中定义抽象基类的第一个模块,而它们又太重要了,因此才被放在这里。它们可以在类型检查中用于指定可调用和可哈希的对象

Reversible

Reversible 通过__reversed__() 实现反转,比如序列前后颠倒

4.1 序列的通用操作

序列是最基本的数据结构,是一块用于存放多个值得连续内存空间,并且按一定顺序排列,每个元素都分配一个索引,通过该索引可以取出相应的值。Python 中内置了 4 个常用的序列结构,分别是字符串str(""或'')列表list([])元组tuple(())range

列结构均有以下几个通用操作

a = “hello word 我是张三”

操作含义与作用案例
+相加“hello” + " word"
*相乘“hello”*3
[index]索引a[0]
[::]切片a[1:3]
x in seq检测 x 是否是序列seq中的成员‘e’ in “hello”
x not in seq检测 x 是否不是序列seq中的成员‘e’ not in “hello”
len(seq)计算序列seq中的成员个数len(“hello”)
Iterable可迭代,既可以循环遍历for item in “hello”
reversed(seq)该函数可以返回一个逆序序列的迭代器reversed(“123”)
seq.index(x)x首次出现在序列中的下标a.index(“l”)
seq.count(x)x在序列中出现的次数a.count(“l”)

4.2 列表

4.2.1 定义

列表是由一系列按特定顺序排列的元素组成,所有的元素都放在一个 [] 中,两个相邻元素间使用逗号,分隔。并且同一个列表中,元素的类型可以不同

在这里插入图片描述

4.2.2 创建

使用list()函数创建列表

  • list() 函数将序列转换成列表

  • range() 函数生成数字序列

  • 可以使用 list( )函数直接将 range() 函数循环出来的序列结果转换为列

# 创建空列表
list1 = []

# 使用list()函数创建列表
# 创建一个[10~20)之间所有偶数的列表
list2 = list(range(10, 20, 2))
print(list2)  # [10, 12, 14, 16, 18]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.2.3 索引

序列中的每一个元素都有索引(或者叫下标),索引是从0开始递增的,即下标为0表示第一个元素,下标为1表示第2个元素,依次类推.

索引可以是负数,从右向左计数,最后一个元素的索引值是-1,倒数第二个元素的索引值为-2,依次类推,通过索引可以访问序列中的任何元素。

在这里插入图片描述

需求:定义一个包含6个元素的列表,要访问它的第3个元素和最后一个元素

# 列表用中括号:[] 表示,定义列表 list, list中有 6 个元素
list = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
# 输出第3个元素
print(list[2])
# 输出最后一个元素
print(list[-1])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.2.4 切片

切片操作可以访问一定范围内的元素。通过切片操作可以生成一个新的序列。

语法格式:seqName[start : end : step]

参数说明:

  • seqName 表示序列的名称
  • start 表示切片的开始的位置(包括该位置),如若不指定,则默认为0
  • end 表示切片结束的位置(不包括该位置),如果不指定,则默认为序列的长度
  • step 表示切片的步长,如果省略,则默认为1,当省略步长时,最后一个冒号也省略

通过切片获取列表 [“张三”, “李四”, “王五”, “赵六”, “孙七”, “周八”] 中第3个到第5个元素,再获取第1个、第3个、第5个元素

如果想复制整个序列,可以省略 startend 参数都省略,但中间冒号需要保留,例如list[:],就表示复制整个名称为 list 的序列

# 列表用中括号:[] 表示,定义列表 list, list中有 6 个元素
list = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
# 输出第3个到第5个元素
print(list[2:5])
# 输出第1个、第3个、第5个元素
print(list[0:5:2])
# 如果想复制整个序列,可以省略 start 和 end 参数都省略,但中间冒号需要保留
# 例如list[:],就表示复制整个名称为 list 的序列
print(list[:])
# 前3个元素
print(list[:3])
# 获取奇数位置的索引元素
print(list[::2])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出:

[‘王五’, ‘赵六’, ‘孙七’]
[‘张三’, ‘王五’, ‘孙七’]
[‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘孙七’, ‘周八’]
[‘张三’, ‘李四’, ‘王五’]

[‘张三’, ‘王五’, ‘孙七’]

4.2.5 相加

Python中,支持两种相同的序列相加操作。即将两个序列连接,使用 + 号运算符实现。例如将列表相加

  • 在进行序列相加时,相同类型的序列是指,同为列表、元组或集合等,序列中的元素可以不同

  • 不能是列表和元组相加,或者是列表与字符串相加等

# 数字类型的列表
num_list = [57, 11, 22, 48]
# 字符串类型的列表
list = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
print(num_list + list)

# 定义两个字符串序列
str1 = "hello"
str2 = " word"
print(str1 + str2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

输出:

[57, 11, 22, 48, ‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘孙七’, ‘周八’]

4.2.6 乘法

使用数字 n 乘以一个序列会生成新的序列。新的序列的内容为原来序列的重复 n 次的结果

需求:将实现一个序列乘以3,生成一个新的序列并输出结果,从而达到 重要的事情说三遍 的效果

list = ["重要的事情说三遍"]
print(list * 3)
str1 = "a"
print(str1 * 6)
  • 1
  • 2
  • 3
  • 4

输出:

[‘重要的事情说三遍’, ‘重要的事情说三遍’, ‘重要的事情说三遍’]
aaaaaa

4.2.7 检查元素是否是list的成员

使用 in 关键字检查某个元素是否是序列的成员,使用 not in 关键字实现检查某个元素是否不包含在指定序列中。

语法格式:value in seqName

参数说明:

  • value:表示要检查的元素
  • seqName:表示指定的序列

需求:检查序列 [“张三”, “李四”, “王五”, “赵六”, “孙七”, “周八”] 中

  • 是否包含元素 “李四”

  • 是否包含元素 “周舞”

  • 是否不包含 “孙七”

list = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
print("李四" in list)
print("周舞" in list)
print("孙七" not in list)
  • 1
  • 2
  • 3
  • 4

输出:

True
False
False

4.2.8 计算序列的长度、最大值、最小值

Python 中,提供了内置内置函数计算序列的长度、最大值、最小值等

# 数字类型的列表
num_list = [57, 11, 22, 48]
# 计算序列长度
print(len(num_list))  # 4
# 序列中的最小元素
print(min(num_list))  # 11
# 序列中的最大元素
print(max(num_list))  # 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.2.9 遍历列表

遍历列表常用的两种方法:

for item in listnamefor循环遍历列表,只能输出元素的值

for index,item in enumerate(listname):实现同时输出索引值和元素

说明:

  • index:用于保存元素的索引
  • item:用于保存获取到的元素值,要输出的元素内容时,直接输出该变量即可
  • listname:列表名称

enumerate() 函数说明:

  • 对于一个可迭代或可遍历的对象,如列表、字符串等,enumerate()将其组成一个索引序列,利用它可以同时获得索引
list_name = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
# 方法1
for item in list_name:
    print(item)

# 方法2
for index, item in enumerate(list_name):
    print(f"{index} - {item}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

输出:

张三
李四
王五
赵六
孙七
周八
0 - 张三
1 - 李四
2 - 王五
3 - 赵六
4 - 孙七
5 - 周八

4.2.10 增删改查

增加数据

  • append() 方法用于在列表末尾追加元素

  • insert() 方法向列表指定位置中添加元素

  • extend() 方法将一个列表中的全部元素添加到另一个列表中,类似 + 号效果

list_name1 = ["张三"]
# 在 list_name1 列表尾部添加元素
list_name1.append("李四")
list_name1.append("王五")
print(list_name1)  # ['张三', '李四', '王五']

list_name2 = ["孙悟空", "猪八戒"]
# 在 list_name2 列表索引位置 1 插入元素
list_name2.insert(1, "xx")
print(list_name2)  # ['孙悟空', 'xx', '猪八戒']

# 将 list_name2 列表中的全部元素添加到 list_name1 列表中
list_name1.extend(list_name2)
print(list_name1)  # ['张三', '李四', '王五', '孙悟空', 'xx', '猪八戒']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出:

[‘张三’, ‘李四’, ‘王五’]
[‘孙悟空’, ‘xx’, ‘猪八戒’]
[‘张三’, ‘李四’, ‘王五’, ‘孙悟空’, ‘xx’, ‘猪八戒’]

删除数据

  • list_name.pop(index)

    index表示索引值,如果不写index参数,默认会删除列表中的最后一个元素,指定索引不存在时,会抛出异常

  • del list_name[index]: 删除指定索引位置的元素

  • del list_name: 列表不再使用时,可以使用 del 语句将其删除

    del 语句在实际开发时,并不常用。因为 Python 自带垃圾回收机制会自动销毁不用列表,所以我们即使不手动删除,Python 也会自动将其回收

  • list_name.remove(): 根据元素值进行删除,删除的元素不存在,则会报错,所以使用该方法删除的元素前,最好判断一下元素是否存在

  • list_name.clear(): 清空列表中的元素

list_name = ["张三", "李四", "王五", "赵六", "孙七", "周八"]

# 删除最后一个元素
# 方法1
delete1 = list_name.pop()
print(delete1)  # 周八
print(list_name)  # ['张三', '李四', '王五', '赵六', '孙七']

# 方法2
# 指定索引删除
delete2 = list_name.pop(-1)
print(delete2)  # 孙七
print(list_name)  # ['张三', '李四', '王五', '赵六']

# 方法3
del list_name[-1]
print(list_name)  # [['张三', '李四', '王五']

# 删除指定元素
# 删除 "李四"
if "李四" in list_name:
    list_name.remove("李四")
else:
    print("list_name 列表中,没有李四")
print(list_name)  # ['张三', '王五']

# 清空所以元素
list_name.clear()
print(list_name)  # []
  • 1
  • 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

修改元素

list_name[index]=value:指定索引元素赋予新值

list_name[start:end]=[xx,xx,xxx]: 切片范围赋予新值,如果索引对应的值不存在,则会新建数据

# 定义一个列表
list_name = [1, 2, 3, 4, 5]

# 将下标为2元素的值修改为 "66"
list_name[2] = "66"
print(list_name)  # [1, 2, '66', 4, 5]

# 将索引 2-6的值设置为 [20,30,40,50,60]
# 如果下标对应的值不存在,则会新建数据
list_name[2:6] = [20, 30, 40, 50, 60]
print(list_name)  # [1, 2, 20, 30, 40, 50, 60]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

输出:

[1, 2, ‘66’, 4, 5]
[1, 2, 20, 30, 40, 50, 60]

查询和其他操作

  • 使用 print(list_name) 函数

  • list_name[index]:通过索引获取指定的元素,索引超出范围会抛出异常

  • list_name[start: end:step]:也可以通过切片操作,来访问列表中的部分元素

  • list_name.count(element):统计某个元素在列表中出现的次数,元素不存在则会抛出异常

  • list_name.index(element):获取元素在列表中首次出现的索引位置

  • list_name.reverse():列表元素前后颠倒,既第一个元素和最后一个元素交互位置,第二个元素和倒数第二个元素交互位置

  • list_name.sort():列表中所有元素默认升序排列

# 定义一个列表
list_name = [54, 89, 90, 88, 54, 69]

# print() 函数 打印列表
print(list_name)

# 通过索引,获取索引下标为2的元素
print(list_name[2])  # 90
# 通过切片,获取第2个到第5个之间的元素,不包括第5个
print(list_name[1:4])  # [89, 90, 88]

# 元素54出现的次数
print(list_name.count(54))  # 2
# 元素54首次出现的索引位置
print(list_name.index(54))  # 0

# 列表前后颠倒
list_name.reverse()
print(list_name)  # [69, 54, 88, 90, 89, 54]

# 默认是升序
list_name.sort()
print(list_name)  # [54, 54, 69, 88, 89, 90]

# 降序
list_name.sort(reverse=True)
print(list_name)  # [90, 89, 88, 69, 54, 54]
  • 1
  • 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

输出:

[54, 89, 90, 88, 54, 69]
90
[89, 90, 88]
2
0
[69, 54, 88, 90, 89, 54]
[54, 54, 69, 88, 89, 90]
[90, 89, 88, 69, 54, 54]

4.2.11 列表推导式

列表推导式是一种简洁的语法,可以快速创建列表

语法格式: [expression for item in iterable if condition]

参数说明:

  • expression:是一个表达式,用于计算每个新元素的值。
  • item:变量,值为表 iterable 中的每个元素。
  • iterable:是一个可迭代对象,可以是列表、元组、集合、字典等
  • condition:是一个可选的条件表达式,用于过滤元素

需求:生成列表 [1,2,3,4,5]、[1,3,5]

# 需求:生成列表 [1,2,3,4,5]、[1,3,5]
list1 = []
# for 循环生成 [1,2,3,4,5]
for item in range(1, 6):
    list1.append(item)
print(list1)

# 列表推导式生成 [1,2,3,4,5]
num_list = [item for item in range(1, 6)]
print(num_list)

# 生成奇数列表 [1,3,5]
num_list_odd = [item for item in range(1, 6) if item % 2 == 1]
print(num_list_odd)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.2.12 二维列表

# 二维数组 两行三列 2x3
arr = [
    [0, 1, 2],
    [0, 1, 2]
]
# 获取元素
print(arr[0])
print(arr[0][2])
print(arr[1][1])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

需求:使用古诗 “床前明月光,疑是地上霜,举头望明月,低头思故乡” 生成 [[‘床’, ‘前’, ‘明’, ‘月’, ‘光’], [‘疑’, ‘是’, ‘地’, ‘上’, ‘霜’], [‘举’, ‘头’, ‘望’, ‘明’, ‘月’], [‘低’, ‘头’, ‘思’, ‘故’, ‘乡’]] 二维数字,并打印到控制台

str1 = "床前明月光"
str2 = "疑是地上霜"
str3 = "举头望明月"
str4 = "低头思故乡"

arr = [list(str1), list(str2), list(str3), list(str4)]
print(arr)
print(len(arr))  # 4

for i in range(len(arr)):
    for j in range(len(arr[i])):
        print(arr[i][j], end="")
    # 换行
    print("")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出:

[[‘床’, ‘前’, ‘明’, ‘月’, ‘光’], [‘疑’, ‘是’, ‘地’, ‘上’, ‘霜’], [‘举’, ‘头’, ‘望’, ‘明’, ‘月’], [‘低’, ‘头’, ‘思’, ‘故’, ‘乡’]]
4
床前明月光
疑是地上霜
举头望明月
低头思故乡

4.2.13 总结

list有如下特点

  • 列表的元素按照顺序有序挨着

  • 索引映射唯一一个数据

  • 列表可以存储重复数据

  • 可以存储任意类型的数据

  • 根据需要动态分配和回收内存

4.3 元组

元组(tuple)是另一个重要的序列结构,与列表类似,由一系列按特定顺序排列的元素组成,但是它是不可变序列, 元素都放在一个()中,两个相邻元素间使用逗号,分隔。

在这里插入图片描述

4.3.1 创建

元组中只包含一个元素时,需要在元素后面添加逗号

# 创建空元组
tuple1 = ()
print(type(tuple1))  # <class 'tuple'>

# 创建一个元素的元组
# 元组中只包含一个元素时,需要在元素后面添加逗号
tuple2 = ("张三",)

# 创建多个元素
tuple3 = ("张三", "李四", [1, 2, 3], False)
# 小跨号也可以省略不写,只有再循环的时候推荐省略
tuple4 = "张三", "李四", [1, 2, 3], False
print(tuple3)
print(tuple4)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出:

<class ‘tuple’>
(‘张三’, ‘李四’, [1, 2, 3], False)
(‘张三’, ‘李四’, [1, 2, 3], False)

使用tuple()函数创建元组

  • tuple() 函数将序列转换成元组

  • range() 函数生成数字序列

  • 可以使用 tuple()函数直接将 range() 函数循环出来的序列结果转换为元组

需求:创建一个[10~20)之间所有偶数的元组

# 创建一个[10~20)之间所有偶数的元组
tuple5 = tuple(range(10, 20, 2))
print(tuple5)  # (10, 12, 14, 16, 18)
  • 1
  • 2
  • 3

4.3.2 通用操作

tuple1 = (1, 2, 3)
tuple2 = (66, ["张三", "李四", "王五"], False, "hell", "hell")
# 通用操作
# 相加
print(tuple1 + tuple2)
# 相乘
print(tuple1 * 3)
# 索引
print(tuple2[1])  # ['张三', '李四', '王五']
# 切片
# 下标[0,3),步长为1
print(tuple2[0:3:1])  # (66, ['张三', '李四', '王五'], False)
# 下标[0,3),步长为2
print(tuple2[0:3:2])  # (66, False)
# 检查元素 "张三" 是否在元组中
print("张三" in tuple2)  # False
print("李四" not in tuple2)  # True
# 计算元组元素个数
print(len(tuple2))  # 5
# 循环遍历
for item in tuple2:
    print(item)
# 元组逆序颠倒,点到后是一个序列
seq = reversed(tuple1)
print(tuple(seq))  # (3, 2, 1)

# 元素首次出现的元组中的下标
print(tuple2.index("hell"))  # 3
# 元素在元组中出现的次数
print(tuple2.count("hell"))  # 2
  • 1
  • 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

4.3.3 删改查

元组一旦初始化完成,不能增加、修改、删除局部元素,但是可以重新整体赋值

删除数据

对于已创建的元组,不再使用时,可以使用 del 语句将其删除

语法格式:del tuple_name, 其中,tuple_name为要删除元组名称

# 创建一个[10~20)之间所有偶数的元组
tuple1 = tuple(range(10, 20, 2))
print(tuple1)  # (10, 12, 14, 16, 18)

del tuple1
# print(tuple1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

修改元素

元组是不可的序列,我们不能对单个元素进行修改,但是可以对元组整体重新赋值

tuple6 = (1, 2, 3)
tuple6 = (4, 5, 6)
print(tuple6)  # (4, 5, 6)
  • 1
  • 2
  • 3

我们说元组是不可变的,为什么下面这种情况元组的值修改了?

原因:字典中的列表list、元组、字典、集合都是存储的地址的引用,而数值、字符串、布尔则是存储的真实值

list1 = ["张三", "李四", "王五"]
tuple2 = (66, ["张三", "李四", "王五"], False, "hell")
print(tuple2)

# 修改列表list1的元组
list1[1] = "xxx"
# 我们说元组是不可变的,为什么这种情况元组的值改变了呢?
print(tuple2)

# 原因:字典中的列表list、元组、字典、集合都是存储的地址的引用,而数值、字符串、布尔则是存储的真实值
# 证明以下,看看下面这个元组的内存模型
tuple3 = (66, 58.44, "hell", False, True, ["张三", "李四", "王五"], (1, 2, 3), {"key1": 123}, {1, 2, 3})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

查询和其他常见操作

  • 使用 print(tuple_name) 函数

  • tuple_name[index] 通过索引获取指定的元素

  • 通过切片tuple_name[start:end:step]操作,来访问元组中的部分元素

  • tuple_name.index(value) 获取某个元素的下标

  • tuple_name.count(element) 统计某个元素在元组中出现的次数

# 创建一个[10~20)之间所有偶数的元组
tuple1 = tuple(range(10, 20, 2))
# 使用 print() 函数 打印列表
print(tuple1)  # (10, 12, 14, 16, 18)
# 通过索引,获取索引下标为1的元素
print(tuple1[1])  # 12
# 通过切片,获取第2个到第5个之间的元素,不包括第5个
print(tuple1[1:4])  # (12, 14, 16)

tuple2 = (54, 89, 90, 88, 54, 69)
# 元素54出现的次数
print(tuple2.count(54))  # 2
# 元素54首次出现的索引位置
print(tuple2.index(54))  # 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出:

(10, 12, 14, 16, 18)
12
(12, 14, 16)
2
0

4.3.4 遍历元组

循环遍历元组

方法一:直接使用 for...in 循环遍历,只能输出元素的值

方法二:使用 for 循环和 enumerate() 函数,可以实现同时输出索引值和元素

# 创建一个[10~20)之间所有偶数的元组
tuple1 = tuple(range(10, 20, 2))
# 循环遍历元素
for item in tuple1:
    print(item)

print("for...in enumerate 遍历")
for index, item in enumerate(tuple1):
    print(f"{index} - {item}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出:

10
12
14
16
18
for…in enumerate 遍历
0 - 10
1 - 12
2 - 14
3 - 16
4 - 18

4.3.5 元组推导式

元组推导式是一种简洁的语法,可以快速创建元组,它的表现形式和列表推导式类似,只是将推导式中的 [] 修改为 ()

语法格式: (expression for item in iterable if condition)

参数说明:

  • expression:是一个表达式,用于计算每个新元素的值。
  • item:变量,值为表 iterable 中的每个元素。
  • iterable:是一个可迭代对象,可以是列表、元组、集合、字典等
  • condition:是一个可选的条件表达式,用于过滤元素

需求:生成元组 (1,2,3,4,5)、(1,3,5)

# 列表推导式生成 (1,2,3,4,5)
tuple1 = tuple((item for item in range(1, 6)))
print(tuple(tuple1))

# 生成奇数列表 (1,3,5)
# tuple2 = (item for item in range(1, 6) if item % 2 == 1)
tuple2 = (item for item in tuple1 if item % 2 == 1)
print(tuple(tuple2))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.4 字符串

  • 字符串是连续的字符序列,用单引号''双引号""三引号'''或"""表示

  • 当字符串包含单引号时,应该使用双引号

  • 当字符串包含双引号时,应该使用单引号

str1 = 'hello word'
str2 = "hello word"
# 当字符串包含单引号时,应该使用双引号
str3 = "it's a string"
# 当字符串包含双引号时,应该使用单引号
str4 = 'hello " word'
print(str3)
print
(str4)

# 多行字符串
str5 = '''
朝辞白帝彩云间,
千里江陵一日还。
两岸猿声啼不住,
轻舟已过万重山。
'''
print(str5)

str6 = """
朝辞白帝彩云间,
千里江陵一日还。
两岸猿声啼不住,
轻舟已过万重山。
"""
print(str6)
  • 1
  • 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

4.4.1 常见操作

字符串操作符,下表示例中 a ="hello ",b ="Python"

操作含义与作用案例
+字符串的拼接,不允许直接与其他类型的数据拼接a+b
*相乘,重复输出字符串a*3
[]通过索引获取字符串中字符a[0]
[::]切片a[1:3]
in字符串中包含给定的字符返回 True‘e’ in a
not in字符串中不包含给定的字符返回 True‘y’ not in a
a = "hello "
b = "Python"
# 拼接
print(a + b)
# 不允许直接与其他类型的数据拼接
# print(a + 1)
# 相乘
print(a * 3)
# 获取下标为2的字符
print(a[2])
# 切片
print(a[1:4])
print(a[1:4:2])

# 是否包含在序列中
print("e" in a)
print("x" in a)

print("n" not in a)
print("x" not in a)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4.4.2 查询

字符串查询 indexfind,建议使用 find,因为 index 没有找到匹配的字符串,方法会报异常

方法名称作用
index(sub[, start[, end]])查找子串 sub 第一次出现的位置,如果不存在抛出异常
rindex(sub[, start[, end]])查找子串 sub 最后一次出现的位置,如果不存在抛出异常
find(sub[, start[, end]])查找子串 sub 第一次出现的位置,如果找到则返回相应的索引,否则返回-1
rfind(sub[, start[, end]])查找子串 sub 最后一次出现的位置,如果不存在返回-1
str1 = "my name is zhangsan,what your name?"
str2 = "name"

# 第一次出现的位置
print(str1.index(str2))  # 3
print(str1.find(str2))  # 3

# 最后一次出现的位置
print(str1.rindex(str2))  # 30
print(str1.rfind(str2))  # 30

# 从第6个元素开始查找,超过元素索引或者没找到,不会报错
print(str1.rfind("xxx", 5, -1))  # -1
print(str1.rfind("xxx", 5, 55))  # -1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.4.3 大小写转换

字符串大小写转换操作

方法名称作用
upper()将字符串中所有字符都转为大写
lower()将字符串中所有字符都转为小写
swapcase()交换大小写。大写转为小写,小写转为大写
capitalize()第一个字符大写,其余小写
title()每个单词的第一个字符大写,其余均为转小写
str1 = "my name is ZHANGsan"
# 将字符串中所有字符都转为大写
print(str1.upper())  # MY NAME IS ZHANGSAN
# 将字符串中所有字符都转为小写
print(str1.lower())  # my name is zhangsan

# 大写转为小写,小写转为大写
print(str1.swapcase())  # MY NAME IS zhangSAN
# 第一个字符大写,其余小写
print(str1.capitalize())  # My name is zhangsan
# 每个单词的第一个字符大写,其余均为转小写
print(str1.title())  # My Name Is Zhangsan
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4.4.4 对齐

字符串对齐操作

方法名称作用
center(width,fillchar)居中对齐,返回一个指定的宽度 width 居中的字符串,fillchar 为填充的字符,默认为空格
ljust(width[,fillchar])左对齐,返回一个原字符串左对齐,并使用 fillchar 填充至长度 width 的新字符串,fillchar 默认为空格。
rjust(width[,fillchar])右对齐,返回一个原字符串右对齐,并使用fillchar(默认空格)填充至长度 width 的新字符串
zfill(width)填充,返回长度为 width 的字符串,原字符串右对齐,前面填充0
str1 = "hello world!"
# 居中对齐
print(str1.center(20, '*'))  # ****hello world!****
# 左对齐
print(str1.ljust(20, '*'))  # hello world!********
# 右对齐
print(str1.rjust(20, '*'))  # ********hello world!
# 填充
print(str1.zfill(20))  # 00000000hello world!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.4.5 分割

分割字符串操作

方法名称作用
split(sep,maxsplit)从左边开始,按照分隔符切分为字符串列表,不包括分隔符。其中 sep 为分隔符,如果不指定分隔符,默认为空字符进行分割;maxspllit 为指定分割次数,如果不指定或者为-1,则分割的次数没有限制
rsplit(sep,maxsplit)从右边开始,按照分隔符切分为字符串列表,不包括分隔符。其中 sep 为分隔符,如果不指定分隔符,默认为空字符进行分割;maxspllit 为指定分割次数,如果不指定或者为-1,则分割的次数没有限制
partition(sep)从左边开始,按照第一个sep,并以 sep 为界,将字符串分割为 3 部分,返回一个新的元组
rpartition(sep)从右边边开始,按照第一个sep,并以 sep 为界,将字符串分割为 3 部分,返回一个新的元组
splitlines()按照换行符进行分割,得到新的列表
str1 = "my name is zhangsan, what is your name"
# 以空格为分隔符
print(str1.split())
# 以 i 为分隔符, 分割一次
# 从左边开始
print(str1.split('i', 1))
# 从右边开始
print(str1.rsplit('i', 1))

# 以 b 为分隔符, 没找到不会报错
print(str1.split('b'))
print(str1.rsplit('b'))

# 从做左边开始,找到第一个name,分割为三部分
print(str1.partition("name"))
# 从做右边开始,找到第一个name,分割为三部分
print(str1.rpartition("name"))

# 按照换行符进行分割
str2 = """my name is qlee
what is your name"""
print(str2.splitlines())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

输出:

[‘my’, ‘name’, ‘is’, ‘zhangsan,’, ‘what’, ‘is’, ‘your’, ‘name’]
['my name ', ‘s zhangsan, what is your name’]
['my name is zhangsan, what ', ‘s your name’]
[‘my name is zhangsan, what is your name’]
[‘my name is zhangsan, what is your name’]
('my ‘, ‘name’, ’ is zhangsan, what is your name’)
('my name is zhangsan, what is your ', ‘name’, ‘’)
[‘my name is qlee’, ‘what is your name’]

4.4.6 合并与替换

字符串合并与替换操作

方法名称作用
str.join(seq)seq中所有的元素,按照指定字符串str,连接为一个新的字符串
replace(old,new[,max])将字符串中的 old 替换成 new, 如果 max 指定,则替换不超过 max
# 元组
seq1 = ("h", "e", "l", "l", "o")
# 列表
seq2 = ["h", "e", "l", "l", "o"]
# 字符串
seq3 = "hello"
# 无分隔符
print("".join(seq1))  # hello
# 空格
print(" ".join(seq1))  # hello
# ","
print(",".join(seq1))  # h,e,l,l,o

str1 = "my name is zhangsan, what is your name"
# 把 name 替换成 xxx 
print(str1.replace("name", "xxx"))  # my xxx is zhangsan, what is your xxx
# 把 name 替换成 xxx,替换 1次
print(str1.replace("name", "xxx", 1))  # my xxx is zhangsan, what is your name

# 注意 str1 本身不发生改变
print(str1)  # my name is zhangsan, what is your name
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.4.7 判断字符串

字符串判断操作

方法含义与作用
isidentifier()判断字符串是不是合法标识符(字符、数字、下划线)
isspace()判断字符是否只有空白字符(回车、换行和水平制表符)
isalpha()判断字符串是否全部由字母组成,注意中文也可以
isdecimal()判断字符是否全部由十进制的数字组成,不包括中文、罗马字符
isdigit()判断字符串只包含数字,不包括中文数字
isnumeric()判断字符串是否全部由数字组成,中文数字也算
isalnum()判断字符串是否由字母和数字组成
islower()判断字符串中的字符是否全部为小写,字符串至少有一个字符
isupper()判断字符串中的字符是否全部为大写,字符串至少有一个字符
istitle()判断字符串是否标题话
isascii()如果字符串为空或字符串中的所有字符都是 ASCII,则返回 True,否则返回 False
isprintable()如果所有字符都是可打印的,则 isprintable() 方法返回 True,否则返回 False
print("hello&".isidentifier())  # False,&为非法标识符
print("   t".isspace())  # False,"t"为非空
print("aldflafd你好1".isalpha())  # ture,中文也可以
print("123四".isdecimal())  # False,中文不属于十进制
print("123四".isnumeric())  # True,中文、罗马字符的数字也算
print("123abc".isalnum())  # True,只能字母和数字
print("123四".isdigit())  # False,不能包括中文
print("".islower())  # False,不能为空字符
print("ZHANGSAN".isupper())  # True
print("My Name Is zhangsan".istitle())  # True,只有第一个字符为大写
print("我是中国人".isascii())  # False,中文不属于ascii
print("Hello!\nAre you ?".isprintable())  # False,\n不可打印
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4.4.8 字符串的比较

字符串的比较操作:

  • 运算符:> , >=, <, <=, ==, !=
  • 比较规则:从第一个以此往下比较
  • 比较原理:比较的是oridinal value(编码值,比如ascii编码,对应ascii编码值)
方法含义与作用
max(str)返回字符串 str 中编码值最大的字符
min(str)返回字符串 str 中编码值最小的字符
ord(str)将指定字符转换为编码值
chr(str)将编码值转换为对应的字符
print(ord("h"))  # 104
print(ord("H"))  # 72
print(chr(104))  # h
print("hello" < "Hello")  # False

str1 = "my name is zhangsan"
max_value = max(str1)
min_value = min(str1)
print(f"max: {max_value}-{ord(max_value)}")  # max: z-122
print(f"min: {min}-{ord(min_value)}")  # min: <built-in function min>-32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4.4.9 去除两端空格和特殊字符

方法含义与作用
strip(chars)去掉左右两边的chars字符,如果chars为空,默认去除空字符
lstrip(chars)去掉左边的chars字符,如果chars为空,默认去除空字符
rstrip(chars)去掉右边的chars字符,如果chars为空,默认去除空字符
str1 = "   my name is zhangsan"
# 去掉两边的空白
print(str1.strip())  # my name is zhangsan
# 去掉右边的"e"字符
print(str1.rstrip("e"))  #    my name is zhangsan

str2 = "!!!!my name is zhangsan!!!!"
# 去掉左右边的"!"字符
print(str2.strip("!"))  # my name is zhangsan
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.4.10 判断开头结尾

判断开头结尾字符串操作

方法含义与作用
startswith(str)判断字符串是不是以 str 开头
endswith(str)判断字符串是不是以 str 结尾
str1 = "my name is zhangsan"
print(str1.startswith("my"))  # True
print(str1.endswith("is"))  # False
  • 1
  • 2
  • 3

4.4.11 编码与解码

字符串的编码与解码操作

方法含义与作用
encode(encoding=“UTF-8”,errors=“strict”)encoding 指定的编码格式编码字符串,如果出错默认报一个ValueError 的异常,除非 errors 指定的是ignore或者replace
bytes.decode(encoding=“utf-8”, errors=“strict”)Python3 中没有 decode 方法,但我们可以使用 bytes 对象的 decode() 方法来解码,这个 bytes 对象可以由字符串的encode() 来编码返回

用什么编码,就用什么解码

str1 = "我是中国人"
str_utf8 = str1.encode("UTF-8")
str_gbk = str1.encode("GBK")

print("------------编码---------------")
print("UTF-8 编码:", str_utf8)
print("GBK 编码:", str_gbk)
print("------------解码---------------")
print("UTF-8 解码:", str_utf8.decode('UTF-8', 'strict'))
print("GBK 解码:", str_gbk.decode('GBK', 'strict'))

# 用什么编码,就用什么解码
# print(str_utf8.decode('GBK', 'strict'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出:

------------编码---------------
UTF-8 编码: b’\xe6\x88\x91\xe6\x98\xaf\xe4\xb8\xad\xe5\x9b\xbd\xe4\xba\xba’
GBK 编码: b’\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb’
------------解码---------------
UTF-8 解码: 我是中国人
GBK 解码: 我是中国人

4.4.12 字符串计数

字符串计数操作

方法含义与左右
count(sub,start=0,end=len(string))统计子串 sub 出现的次数,如果不存在抛出异常
内置函数 len()len()不是 string 的方法,是内置函数,计算字符串中的字符个数

Python 中,数字、英文、小数点、下划线和空格占一个字节,一个汉字可能占2-4个字节。汉字在GBK/GB2312编码中占2个字节,在UTF-8/unicode编码中占3个字节或4个字节

str1 = "my name is zhangsan, what is your name?"
print(str1.count("name"))  # 2
print(len(str1))  # 39

str2 = "人生苦短,我用Python"
"""
说明:通过len()函数计算字符串的长度时,不区分英文、数字和汉字,
所有字符都按照一个字符计算
"""
print(len(str2))  # 13

# 获取utf-8编码的字符串长度
str_len = len(str2.encode())
# 6x3+7
print(str_len)  # 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

4.4.13 f-string 格式化

字符串格式化有好几种方式,Python 3.6 版本开始引入 f-string 格式化字符串,成为了 Python 字符串格式化的首选方法,该方式简单直观、灵活且易于维护。

语法格式:f"{content:format}"

使用以字母f开头的特殊字符串来创建一个字符串模板,用大括号{}括起来的表达式会在运行时被替换成要输出的值。

参数说明:

  • content:替换并填入字符串的内容,可以是变量、表达式或函数等,

  • format:格式描述符,可以省略,后面详细讲述

name = "张三"
str1 = f"我的名字叫:法外狂徒{name}"
print(str1)
  • 1
  • 2
  • 3

format 格式描述符

语法:[[fill]align] [sign] [#] [width] [.precision] [type]

参数描述:

所有参数均为可选参数

fill:用于指定空白出填充的字符

align:用于指定对齐方式,需要和 width 配合使用

sign:用于指定有无符号的数,仅适用于数值类型,正数前加正号(+),负数前加负号(-

#:用于是否显示进制前缀,二进制前缀0b,八进制前缀0o,十六进制前缀0x

width:用于指定所占宽度

precision:用于指定保留的小数位数

type:用于指定类型

align 对齐

format:格式描述符含义与作用
<左对齐(字符串默认对齐方式)
>右对齐(数值默认对齐方式)
^居中

type 基本格式类型

格式描符含义与作用
s对字符串类型格式化
c将十进制整数转换成对应 unicode 编码字符
b将十进制整数转换成 二进制表示
o将十进制整数转换成 八进制表示
d十进制整数
x或X将十进制整数转换成 十六进制格式
f或F转换成浮点数,默认保留小数点后6
e或E科学计数格式,以 e 表示 ×10^
g或G自动在ef 或者 EF中切换,既浮点数和科学计数法之前相互切换显示
%显示百分号,默认显示小数点后6
pi = 3.1415926
# 保留2为小数
print(f"{pi:.2f}")
# 宽度10个字符,数值默认右对齐
print(f"{pi:10.2f}")

# 宽度10个字符,设置右对齐
print(f"{pi:>10.2f}")
# 宽度10个字符,设置右对齐,左边不足用'x'表示
print(f"{pi:x>10.2f}")

# 宽度10个字符,设置左对齐
print(f"{pi:<10.2f}")
# 宽度10个字符,设置左对齐,右边不足用'x'表示
print(f"{pi:x<10.2f}")

"""
需求:打印如下金字塔
          *  
         *** 
        *****
"""
star = "*"
for i in range(1, 6, 2):
    print(f"{star * i:^5}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

输出:

3.14
3.14
3.14
xxxxxx3.14
3.14
3.14xxxxxx

a = 123
print(a)

# 将十进制整数转换成 二进制表示
print(f"{a:#0b}")
# 将十进制整数转换成 八进制表示
print(f"{a:#0o}")
# 将十进制整数转换成 十六进制格式
print(f"{a:#0x}")

b = 0.64254
# 分百分表示
print(f"{b:%}")
print(f"{b:.2%}")
# 百分比格式,数字自动乘上100后按 f 格式排版,并加 % 后缀,加f反而报错
# print(f"{b:.2%f}")

c = 270.51
# 科学计数法格式的字符串
e1 = f"{c:e}"
print(e1)
# 科学计数法和浮点数切换显示
print(f"{float(e1):g}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

输出:

123
0b1111011
0o173
0x7b
64.254000%
64.25%
2.705100e+02
270.51

type 日期类型

格式描符含义与作用显示样例
%Y表示四位数的年份2023年:2023
%y表示两位数的年份2023年:23
%m表示月份,范围 01-12二月:02
%d表示的每月的第几天,范围 01-316号:06
%H24小时制小时数,范围 0-23下午5点:17
%M表示分钟数,范围 00-59第4分钟:04
%S表示秒数,范围 00-59第5秒:05

我们日期常见的格式

  • %Y-%m-%d 如:2023-09-24

  • %Y-%m-%d %H:%M:%S 如:2023-09-24 17:25:34

# 导入日期模块
import datetime

# 获取当前日期
time_new = datetime.datetime.now()
print(time_new)

# f-string 格式化时间
time1 = f"{time_new:%Y-%m-%d %H:%M:%S}"
print(time1)

time2 = f"{time_new:%Y-%m-%d }"
print(time2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4.5 字典

字典是有序的可变序列,不支持索引和切片。保存内容是以 键-值对(也叫 key-value) 的形式存放,可存储任意类型对象,如字符串、数字、元组等。

创建字典,在 之间使用冒号分割,相邻两个元素使用逗号分隔,所有的元素放在一个大括号里{ }中。

在这里插入图片描述

语法格式: dictionary = {"key1":"value1","key2":"value2"}

参数说明:

  • dictionary:表示字典名称
  • key1key2:表示元素的键,必须是唯一的,并且不可变的,可以是字符串、元组或者数字
  • value1value2:表示元素的值,可以是任何数据,不是必须唯一

4.5.1 字典的特征

  • 通过键读取数据,不能通过索引来获取

  • 字典是无序的、可变的,并且可以任意嵌套

  • 字典的键必须唯一

  • 字典中的键必须不可变

    键是不变的,因此可是使用数字、字符串、元组,不能使用列表

4.5.2 创建

# 使用 {} 创建字典
dict1 = {}
dict2 = {"zhansgan": 20, "lisi": 33, "wangwu": 60}
print(dict2)

# 使用 dict() 函数创建字典
dict3 = dict(zhansgan=20, lisi=33, wangwu =60)
print(dict3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.5.3 增删改查

增加/修改数据

  • 语法格式: dict_name[key]=value

    如果 key 不存在,则为新增数据,如果 key 存在,则为修改数据

  • 向字典增加 key-value 键值对: dict_name.update()

# 定义一个字典
dict1 = {"name": "张三", "age": 20, "sex": "男"}
# 增加数据
dict1["height"] = "170CM"
print(dict1)

dict1.update({"nickname": "法外狂徒"})
print(dict1)

# 修改数据
dict1["height"] = 170
print(dict1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出:

{‘name’: ‘张三’, ‘age’: 20, ‘sex’: ‘男’, ‘height’: ‘170CM’}
{‘name’: ‘张三’, ‘age’: 20, ‘sex’: ‘男’, ‘height’: ‘170CM’, ‘nickname’: ‘法外狂徒’}
{‘name’: ‘张三’, ‘age’: 20, ‘sex’: ‘男’, ‘height’: 170, ‘nickname’: ‘法外狂徒’}

删除数据

  • dict_name.pop(): 指定key删除键值对,并返回删除的值

  • del dict_name[key]: 删除字典中指定 key 的键-值对

  • del dict_name: 字典不再使用时,可以使用 del 语句将其删除

# 定义一个字典
dict1 = {"name": "张三", "age": 20, "sex": "男"}

# 删除 key 为 name 的键值对数据
value = dict1.pop("name")
print(value)
print(dict1)

# 删除 key 为 age 的键值对数据
del dict1["age"]
print(dict1)

# 清空字典
dict1.clear()
print(dict1)

# 彻底删除 dict1
del dict1
# 彻底删除之后,则不能再使用,否则会报错
# print(dict1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出:

张三
{‘age’: 20, ‘sex’: ‘男’}
{‘sex’: ‘男’}
{}

查询和其他常见操作

方法或函数解释
print(dict_name)将字典的内容输出
内置函数 len(dict_name)计算字典中键值对个数
dict_name[key]获取指定的key对应的值,如果key不存在,则会报错
dict_name.get()获取指定的key对应的值,如果key不存在,返回空None, 推荐使用该方法
dict_name.keys()获取字典中所有key
dict_name.values()获取字典中所有value
dict_name.items()获取字典中所有key,value
# 定义一个字典
dict1 = {"name": "张三", "age": 20, "sex": "男"}
print(dict1)

# 计算字典中键值对个数
print(len(dict1))  # 3

# 获取指定的 key 为 name 对应的值
print(dict1["name"])  # 张三
# key 为 xxx 不存在,会报错
# print(dict1["xxx"])

print(dict1.get("age"))  # 20
# 如果 key 不存在,返回空 None
print(dict1.get("xxx"))  # None

# 获取所有 key
keys = dict1.keys() # ['name', 'age', 'sex']
print(type(keys))  # <class 'dict_keys'>
print(keys)  # dict_keys(['name', 'age', 'sex'])

# 获取所有 value
values = dict1.values()  # ['张三', 20, '男']
print(type(values))  # <class 'dict_values'>
print(values)  # dict_values(['张三', 20, '男'])

# 获取字典中所有 key-value 键值对
items = dict1.items()  # [('name', '张三'), ('age', 20), ('sex', '男')]
print(type(items))  # <class 'dict_items'>
# dict_items([('name', '张三'), ('age', 20), ('sex', '男')])
print(items)
  • 1
  • 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

输出:

{‘name’: ‘张三’, ‘age’: 20, ‘sex’: ‘男’}
3
张三
20
None
<class ‘dict_keys’>
dict_keys([‘name’, ‘age’, ‘sex’])
<class ‘dict_values’>
dict_values([‘张三’, 20, ‘男’])
<class ‘dict_items’>
dict_items([(‘name’, ‘张三’), (‘age’, 20), (‘sex’, ‘男’)])

  • 如果想将列表的内容输出比较简单,可以直接使用 print() 函数

  • dict_name.get() 方法获取指定的 key 对应的值

list_name = ["元素1", "元素2", "元素3", "元素4", "元素5"]
# print() 函数 打印列表
print(list_name)  # ['元素1', '元素2', '元素3', '元素4', '元素5']
# 通过索引,获取索引下标为2的元素
print(list_name[2])  # 元素3
# 通过切片,获取第2个到第5个之间的元素,不包括第5个
print(list_name[1:4])  # ['元素2', '元素3', '元素4']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.5.4 遍历字典

遍历字典常用的两种方法:

方法一:

  • for item in dict_name.items()

  • for key,value in dict_name.items()

方法二:for index, key in enumerate(dict_name)

# 定义一个字典
dict1 = {"name": "张三", "age": 20, "sex": "男"}

for item in dict1.items():
    print(item)

for (key, value) in dict1.items():
    print(key, value)

# 简写
for key, value in dict1.items():
    print(key, value)

for item in enumerate(dict1):
    print(item)

for index, key in enumerate(dict1):
    print(index, key)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

输出:

(‘name’, ‘张三’)
(‘age’, 20)
(‘sex’, ‘男’)
name 张三
age 20
sex 男
name 张三
age 20
sex 男
(0, ‘name’)
(1, ‘age’)
(2, ‘sex’)
0 name
1 age
2 sex

4.5.5 字典推导式

字典推导式是一种简洁的语法,可以快速创建字典

在这里插入图片描述

names = ["张三", "李四", "王五"]
ages = [18, 30, 66]
# 拉链法
# 生成的是一个序列
zip_data = zip(names, ages)
print(type(zip_data))
print(zip_data)
# 使用内置函数 list(),将序列转换成列表
print(list(zip_data))  # [('张三', 18), ('李四', 30), ('王五', 66)]

# 字典的推导式
dict1 = {key: value for (key, value) in zip(names, ages)}
# 简写
dict2 = {key: value for key, value in zip(names, ages)}
print(dict1)
print(dict2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

<class ‘zip’>
<zip object at 0x0000018C39E14F80>
[(‘张三’, 18), (‘李四’, 30), (‘王五’, 66)]
{‘张三’: 18, ‘李四’: 30, ‘王五’: 66}
{‘张三’: 18, ‘李四’: 30, ‘王五’: 66}

4.5.6 底层原理

在这里插入图片描述

4.6 集合

Python 中,用 set 来表示一个无序不重复元素的序列,作用就是用来给数据去重

4.6.1 创建

可以使用大括号 { } 或者 set() 函数创建集合,但是如果创建一个空集合必须用 set() 而不是 { },因为 { } 是用来表示空字典类型。set 里面不可以存放 list 类型。

# 创建空集合
set1 = set()
set2 = set("hello word")
set3 = set([1,2,3,3])
set4 = set(("张三", "张三"))
# 使用 {} 创建集合,可以有元组,但不能有list
set5 = {"Python", 20, False,("张三", "张三"), 20}
set6 = {5, 8, 6, 1, 1, 5}

print(set1)
print(set2)
print(set3)
print(set4)
print(set5)
print(set6)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出:

set()
{’ ', ‘d’, ‘r’, ‘w’, ‘e’, ‘l’, ‘o’, ‘h’}
{1, 2, 3}
{‘张三’}
{False, ‘Python’, 20, (‘张三’, ‘张三’)}
{8, 1, 5, 6}

4.6.2 增删

增加数据

set_name.add(element):增加元素,可以添加元组,但不能添加list

set_name.update(obj):可以添加元组、列表、字典、字符串等,但是添加的数据为字符串时,会拆分为单个字符然后添加到集合中

# 创建空集合
set1 = set()
set1.add("张三")
set1.add(11)
set1.add(False)
set1.add(("张三", 20))
# 不能添加list,因为list是可变的hash
# set1.add([1,2,3])
print(set1)

set1.update([4, 5, 6])
set1.update((7, 8, 9))
# 只取了字典的key
set1.update({"李四": 25})
# 添加的数据为字符串时,会拆分为单个字符然后添加到集合中
set1.update("hello word")
print(set1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

输出:

{False, 11, ‘张三’, (‘张三’, 20)}
{False, ‘r’, 4, 5, 6, 7, 8, 9, 11, ‘l’, ‘e’, ‘h’, ‘w’, ‘d’, ‘李四’, (‘张三’, 20), ’ ', ‘o’, ‘张三’}

删除数据

  • set_name.remove(element):删除指定元素,元素不存在则抛出异常

  • set_name.remove(element):删除指定元素,元素不存在不抛出异常

  • set_name.pop():删除集合中的第一个元素,并返回被删除的元素

  • set_name.clear():清空集合元素

  • del set_name: 删除整个集合

# 定义一个字典
set1 = {"Python", 20, False, ("张三", "张三"), 20,"李四"}

# 删除指定元素,不存在则抛出异常
set1.remove(20)
print(set1)

# 删除指定元素,不存在不抛出异常
set1.discard(25)
set1.discard(False)
print(set1)

# 删除集合中的第一个元素,并返回被删除的元素
print(set1.pop())
print(set1.pop())
print(set1)

# 清空集合
set1.clear()
print(set1)

# 删除集合
del set1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

{False, (‘张三’, ‘张三’), ‘李四’, ‘Python’}
{(‘张三’, ‘张三’), ‘李四’, ‘Python’}
(‘张三’, ‘张三’)
李四
{‘Python’}
set()

4.6.3 交集、并集、差集

# 集合中数学的操作
set1 = {10, 20, 30, 40}
set2 = {20, 30, 40, 50, 60}

# 1.交集
print(set1.intersection(set2))  # {40, 20, 30}
print(set1 & set2)  # {40, 20, 30}

# 2.并集
print(set1.union(set2))  # {40, 10, 50, 20, 60, 30}
print(set1 | set2)  # {40, 10, 50, 20, 60, 30}

# 3.差集
print(set1.difference(set2))  # {10}
print(set1 - set2)  # {10}

# 4.对称差集
print(set1.symmetric_difference(set2))  # {50, 10, 60}
print(set1 ^ set2)  # {50, 10, 60}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出:

{40, 20, 30}
{40, 20, 30}
{40, 10, 50, 20, 60, 30}
{40, 10, 50, 20, 60, 30}
{10}
{10}
{50, 10, 60}
{50, 10, 60}

4.7 列表、元组、字典和集合的区别

列表元组字典集合
名称listtupledictset
定义符号[](){key:value}{}
是否可变可变不可变可变可变
是否可重复可重复可重复可重复不可重复
是否有序有序有序3.7+ 有序无序
存储方式键-值对(键不可以重复)
索引支持支持不支持不支持
切片支持支持不支持不支持
+,*支持支持不支持不支持
in / not in支持支持支持支持

第5章 函数

5.1 函数定义与调用

在这里插入图片描述

# 定义函数:两个数相加,并返回相加后的结果
def add(x, y):
    z = x + y
    return z


# 调用函数
a = 10
b = 30
print(add(a, b))  # 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.2 参数

Python 中的对象可分为不可变对象(数字、字符串、元组等)和可变对象(列表、字典、集合等);对应地Python 函数的参数传递有以下两种情况

5.2.1 形参和实参

实参为不可变对象(值传递): 当实参为不可变对象时,函数调用是将实参的值复制一份给形参。在函数调用中修改形参时,不会影响函数外面的实参

实参为可变对象(引用传递):当实参为可变对象时,函数调用是将实参的引用复制给形参。在函数调用中修改形参时,函数外面的实参值也会随之变化

# 定义函数:向列表中添加数据
def list_add(list_name, data):
    list_name.append(data)
    print(list_name)  # [11, 22, 33, 20]

# 定义列表
list1 = [11, 22, 33]
num = 20
# 向列表中添加数据20
list_add(list1, num)
# 打印
print(list1)  # [11, 22, 33, 20]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5.2.2 函数传递参数的内存分析

在这里插入图片描述

5.2.3 参数传递

Python 中函数传递参数的形式主要有以下4种,分别为位置传递、关键字传递、默认值传递、可变参数

  1. 位置参数:也称必备参数,必须按照正确的顺序传递到函数中,既调用时的数量和位置必须和定义的一样
def fun1(a,b,c) 
  return a+b+c 
# 位置参数传递
print(fun1(1,2,3))
# 位置参数调用时候,参数数量必须和定义的一致
# print(fun1(1,2))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 关键字参数:根据每个参数的名字传递参数,不用遵守位置的对应关系
def fun1(a, b, c):
    return a + b + c

# 关键字参数传递,不用遵守位置的对应关系
print(fun1(1, c=3, b=2))
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 参数默认值:在定义函数时,直接指定形参的默认值;当没有传入参数时,则直接使用定义函数时设置的默认值

再定义函数时,指定默认值的参数必须在最后(如果有可变参数则放在可变参数的前面),否则产生语法错误

def fun2(a, b, c=10):
      return a + b + c

# 参数默认值
  print(fun2(1, 2))  # 1+2+1=13
  print(fun2(1, 2, 3))  # 1+2+3=6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 可变参数: 可变参数主要有两种形式,一种是 *args,另外一种是 **args

*args: 表示接收任意多个位置参数并将其放到一个元组中

**args: 表示接收任意多个关键字参数并将其放到一个字典中

注意: 如果一个函数中,同时有 *args 和 **args,则 *args 必须放在最前面

def fun3(*name):
    print(type(name))  # <class 'tuple'>
    print(name)  # ('张三', '李四', '王五') 


def fun4(**name):
    print(type(name))  # <class 'dict'>
    print(name)  # {'zhangsan': '张三', 'age': 18, 'sex': '男'}


fun3("张三", "李四", "王五")
fun4(zhangsan="张三", age=18, sex="男")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
"""
1.如果一个函数中,同时有 *args 和 **args,则 *args 必须放在最前面
2.同时有位置参数和关键字参数,则关键字参数必须放在位置参数后面
3.**args: 同名键绑定到对应的具名参数上,剩下的则被 **args 捕获
"""


def f(*args1, x: int = None, y: int, **args2):
    print(f"args1:{args1}")
    print(f"x = {x}")
    print(f"y = {y}")
    print(f"args2:{args2}")


f(1, 2, 3, 4, zhangsan="张三", age=18, sex="男", x=1, y=2)
f(1, 2, 3, 4, x=1, zhangsan="张三", age=18, sex="男", y=2)
f(1, 2, 3, zhangsan="张三", age=18, sex="男", y=2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

5.3 匿名函数

匿名函数就是没有名字的函数,在 Python 中用 lambda 表达式创建匿名函数

语法:result = lambda [参数1,参数2,...]:expression

参数说明:

  • result:用于调用lambda表达式
  • expression:必选参数,用于指定一个实现具体功能的表达式,表达式只能有一个,只能返回一个值。冒号后面一定要是表达式
def add1(x, y):
  return x + y

# 匿名函数
add2 = lambda x, y: x + y

print(add1(10, 20))  # 30
print(add2(10, 20))  # 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5.4 内置高阶函数、推导式

5.4.1 map 映射

在这里插入图片描述

# 原始方法
def square(data):
    list2 = []
    for x in data:
        result = x * x
        list2.append(result)
    return list2


list1 = [1, 2, 3, 4, 5]
list3 = square(list1)
print(list3)


def square1(x):
    return x * x


# map映射
map1 = map(square1, list1)
print(list(map1))

# map映射 + 匿名函数
map2 = map(lambda x: x * x, list1)
print(list(map2))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

5.4.2 filter 过滤

语法格式:filter(function_or_None, iterable)

参数说明:

function_or_None:如果是函数,作用是对 iterable 中的每个元素判断是否符合特定条件,然后返回 TrueFalse,最后将返回 True 的元素放到新列表中;如果不调用任何函数,只对可迭代对象中的元素本身判断真假,保留为真的元素

iterable:可迭代对象(序列、字典等)

在这里插入图片描述

list1 = [1, 2, 3, 4, 5]
# filter 过滤: 过滤掉偶数
# 每个元素作为参数传递给函数进行判断,然后返回 True 或 False
# 最后将返回 True 的元素放到新列表中
list2 = list(filter(lambda x: x % 2 == 1, list1))
print(list2)  # [1, 3, 5]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

5.4.3 列表推导式与map和filter的比较

由于引入列表推导式和生成器表达式,因此map、filter变得没有那么重要了,引入列表推导式和生成器表达式兼具map 和 filter两个函数的功能,而且代码可读性更高

str1 = "abc123"
ascii_1 = [ord(s) for s in str1 if ord(s) > 65]
print(ascii_1)  # [97, 98, 99]

# filter 内置函数
ascii_2 = list(filter(lambda c: c > 65, map(ord, str1)))
print(ascii_2)  # [97, 98, 99]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.5 变量的作用域

局部变量:在函数内部定义的变量。这个变量只能在定义这个变量的函数内部使用

全局变量:在函数外部定义的变量。所有函数内部和外部都可以使用这个变量(有两种方式,第一种定义在函数外;第二种定义在函数内,用 global 关键字修饰)

# age 为全局变量,则函数内外都可以使用
age = 18
print(age)


def fun1():
    print(f"年龄 {age}")


fun1()


def fun2(x, y):
    z = x + y
    print(z)
    global sex
    sex = "男"


fun2(1, 2)
# 因为 z 为函数内的局部变量,只在函数内部有作用,函数外超出了作用域
# print(z)
print(sex)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

5.6 装饰器与闭包

第6章 面向对象

6.1 面向对象编程介绍

如今主流的软件开发思想有两种:一个是面向过程,另一个是面向对象。面向过程出现得较早,典型代表为C语言,开发中小型项目的效率很高,但是很难适用于如今主流的大中型项目开发场景。面向对象则出现得更晚一些,典型代表为JavaC++等语言,更加适合用于大型开发场景。两种开发思想各有长短。

对于面向过程的思想: 需要实现一个功能的时候,看重的是开发的步骤和过程,每一个步骤都需要自己亲力亲为,需要自己编写代码(自己来做)

对于面向对象的思想:当需要实现一个功能的时候,看重的并不是过程和步骤,而是关心谁帮我做这件事(偷懒,找人帮我做)

生活举例:

洗衣服

面向过程(手洗):脱衣服、找一个盆、加水、加洗衣粉、浸泡30分钟、搓洗、拧衣服、倒掉水、再加水、漂洗、拧衣服、倒掉水、晾衣服。

面向对象(机洗):脱衣服、放入洗衣机、按下开关、拿出衣服晾晒。

买电脑

面向过程(自己买):需要电脑、查询参数信息、横向比较机型、了解打折信息、与店家讨价还价、下单、收快递、开机验货、确认收货。

面向对象(找人买):需要电脑、找秘书帮我买、收电脑。

面向对象的三大特征有:封装、继承、多态

6.2 类和对象

类:抽象的,比如一张 “手机设计图”

对象:具体的,比如一部 “真正的手机,比如华为mate50”

类由有属性、行为两个组成部分

属性:事物的特征描述信息,用于描述某个特征 “是什么”

行为:事物 “能做什么”

案例:

人类设计:

  • 事物名称(也叫类名): 人(Person)

  • 属性: 姓名、年龄(age)、性别(sex)等

  • 行为(也叫方法): 跑(run)

狗类的设计:

  • 类名: 狗(Dog)

  • 属性: 品种、毛色、性别、名字

  • 方法: 叫、跑、咬人、吃、摇尾巴等

6.3 类的定义与使用

类的组成:

属性(又叫成员变量):分为共享属性、实列属性两种

方法(又叫成员方法):实列方法、静态方法、类方法

# 人类
# Person 为类名,按大驼峰命名:每个单词首字母大写,其他字母小写
class Person:
    # 共享属性
    address = "地球村"

    # 每次是实列化对象,都要调用该方法,如果不创建,则默认提供一个
    def __init__(self, name, age, sex):
        print("初始化")
        # 属性
        self.name = name
        self.age = age
        self.sex = sex

    # 方法: 类中的函数叫方法
    def run(self):
        print("跑步")

    # 静态方法
    @staticmethod
    def static_method():
        print("静态方法")

    # 类方法
    # 如果方法访问了类中的其他变量或方法,那么就使用@classmethod
    @classmethod
    def class_method(cls): 
        print(f"类方法中,获取共享变量 address = {cls.address}")


# 类外的函数
def eat():
    pass


person1 = Person("张三", 20, "男")  # 初始化
person2 = Person("李四", 19, "女")  # 初始化

print("------共享属性------")
print(person1.address)  # 地球村
print(person2.address)  # 地球村
print(Person.address)  # 地球村

print("------属性------")
print(person1.name)
print(person2.name)

print("------执行方法------")
person1.run()
person1.static_method()
person1.class_method()

Person.static_method()
Person.class_method()
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

6.4 类的内存模型

在这里插入图片描述

6.5 访问限制与Getter、Setter

访问限制

名称列子含义
public 公共的没有限制
private 私有的双下划线开头双下划线表示private(私有)类型的成员,只允许定义该方法的类本身进行访问,而且也不能通过类的实例进行访问
protected 受保护的单下划线开头单下划线开头的表示protected(保护)类型的成员,只允许类本身或子类访问
# 人类
class Person:
    # 共享属性
    address = "地球村"

    # 每次是实列化对象,都要调用该方法,如果不创建,则默认提供一个
    def __init__(self, name, age, sex):
        print("初始化")
        # 属性
        self.name = name
        self._age = age
        self.__sex = sex


person1 = Person("张三", 20, "男")  # 初始化
print(person1.name)  # 张三
print(person1._age)  # 20
# 私有属性,只允许定义该方法的类本身进行访问,而且也不能通过类的实例进行访问
# print(person1.__sex)
print(person1._Person__sex)

# 修改成员
# 修改公共的成员
person1.name = "李四"
print(person1.name)  # 李四

# 修改 受保护的成员
person1._age = 21
print(person1._age)  # 21

# 这种写法可以的意思,是因为给实列增加了一个 ”__sex“ 属性字段,
# 和我们初始化的私有属性”__sex“不是同一个
person1.__sex = "女"
print(person1.__sex)
  • 1
  • 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

Getter、Setter

访问和修改成员变量

# 人类
class Person:
    # 共享属性
    address = "地球村"

    # 每次是实列化对象,都要调用该方法,如果不创建,则默认提供一个
    def __init__(self, name, age, sex):
        print("初始化")
        # 属性
        self.name = name
        self._age = age
        self.__sex = sex

    # getter,获取性别值
    def getSex(self):
        return self.__sex

    # setter,设置性别值
    def setSex(self, value):
        self.__sex = value


person1 = Person("张三", 20, "男")  # 初始化
person1.setSex("女")
print(person1.getSex())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

@property属性计算

经过@property修饰器修饰之后的方法,类实例化后的对象调用该方法时后面变不再需要加上小括号了,看起来就像是调用了类中的一个属性一样

# 人类
class Person:
    # 共享属性
    address = "地球村"

    # 每次是实列化对象,都要调用该方法,如果不创建,则默认提供一个
    def __init__(self, name, age, sex):
        print("初始化")
        # 属性
        self.name = name
        self._age = age
        self.__sex = sex
    
    # getter
    @property
    def sex(self):
        return self.__sex
    
    # setter
    @sex.setter
    def sex(self, value):
        self.__sex = value


person1 = Person("张三", 20, "男")  # 初始化
person1.sex = "女"
print(person1.sex)   # 女
  • 1
  • 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

6.6 封装、继承

封装

就是我写了一个类,我将一个类的属性、方法全部包装到了一个类中。我对类中某些方法和属性进行了隐藏,因为我不想让外部了解我的实现机理或属性,但是会留出一些公开的方法来供外部间接调用这些封装 好的属性和方法,这就是封装!

继承

  1. object是所有类的父类,如果一个类没有继承任何类,则默认继承object

  2. 双下划线开头的成员属性(__xx),最终实列话对象后,成员属性变成 “_类名__xx”

  3. 公共成员变量、受保护的成员变量,谁实列化,属性就挂在该实列化对象身上

  4. 有单下划线的开头成员属性,子类可以直接访问;有双下划线开头的成员属性,子类不能访问;公共的成员变量和共享变量,子类可以任意访问

  5. 有继承关系的对象实列化,调用方法和属性的时候,依次查询获取:实例化对象-> 实例化对象的类 ->父类

  6. 如果子类对继承自父类的某个方法不满意,可以在子类中对其进行重新编写;子类重写后的方法中可以通过super().xxx()调用父类中被重写的方法

  7. Python中,没有方法的重载,为什么呢?

# 父类 Person 继承
class Person(object):
    # 共享成员变量
    address = "地球村"

    def __init__(self, name, age, sex):
        # 实列成员变量
        self.name = name
        self._age = age
        self.__sex = sex

    def info(self):
        print("我是地球人")

    def run(self):
        print("我会跑路")

    @property
    def sex(self):
        return self.__sex

    @sex.setter
    def sex(self, value):
        self.__sex = value


class Student(Person, object):
    grade = "成都七中"

    def __init__(self, name, age, sex, score):
        super().__init__(name, age, sex)
        self.score = score

    def info(self):
        super().info()
        print(
            f"我的名字叫:{self.name}, 今年:{self._age}岁, 我是一个{self.sex}生,生活在{self.address}-{self.grade}中学")


class Teacher(Person, object):
    subject = "语文"

    def __init__(self, name, age, sex, like):
        super().__init__(name, age, sex)
        self.like = like

    def info(self):
        super().info()
        print(
            f"我的名字叫:{self.name}, 今年:{self._age}岁, 我是一个{self.sex}生,生活在{self.address},我是一名{self.subject}老师,我的兴趣是{self.like}")


# 实列化对象
student = Student("张三", 17, "男", 80)
teacher = Teacher("李若风", 30, "女", "旅游")

student.info()
teacher.info()
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

接口、协议、抽象基类、虚拟子类

Python 没有多态,因为 Python 自诞生以来默认使用的鸭子类型设计理念

  • 鸭子类型:忽略对象的类型,转而关注对象有没有实现所需要的方法、签名、语义, Python 自诞生以来默认使用的鸭子类型, 鸭子协议是Python核心思想

  • 抽象基类:指的是collections.abc和自定义的抽象基类

    Pythonabc模块中,ABCABCMeta是两个关键类,用于创建抽象基类(Abstract Base Class

    1. ABC是一个可继承的基类,用于定义抽象基类。

    2. 当一个类继承自ABC时,可以通过使用@abstractmethod装饰器来定义抽象方法。

    3. 抽象方法是一种声明,用于指示子类必须实现这些方法。

    4. 子类必须实现抽象基类中定义的所有抽象方法,否则在实例化时会引发TypeError异常

    5. ABC类本身并不强制要求实现任何方法或属性,它主要用于定义抽象方法和作为抽象基类的标识

    6. ABCMeta是一个元类(metaclass),用于定义抽象基类的元信息。

    7. 通过将ABCMeta作为元类,可以在类定义阶段对类进行检查和修饰。

    8. ABCMeta通过在类定义中使用metaclass参数或在类的基类列表中包含ABCMeta来指定

    9. ABCMeta元类提供了一些功能,例如检查子类是否实现了抽象方法、注册具体实现类等

    总结:

    • ABC是一个可继承的基类,用于定义抽象基类,并通过装饰器@abstractmethod定义抽象方法

    • ABCMeta是一个元类,用于定义抽象基类的元信息,并提供了一些功能来检查和修饰类定义

  • 大鹅类型:在鸭子类型的基础上补充的大饿类型, 自 Python 2.6 开始,由抽象基类支持的方式,该方式会在运行时检查对象是否符合抽象的基本要求,大鹅类型的一个基本特征是,即便不继承,也有办法把一个类注册为抽象类的虚拟子类。虚拟子类实现抽象基类的的全部抽象方法,同样达到继承的效果,也符合鸭子类型。

    大鹅类型要求:

    1. 定义抽象基类的子类,明确表明你在实现抽象基类的接口
  • 虚拟子类:不通过继承,把一个类注册为抽象基类的虚拟子类。注册虚拟子类的方式是在抽象基类上调用 register() 方法。这么做之后,注册的类会变成抽象基类的虚拟子类,但是注册的类不会从抽象基类中继承任何方法或属性。通过鸭子类型实现抽象基类的对应全部方法即可

    虚拟子类不会继承注册的抽象基类,而且任何时候都不会检查它是否符合抽象基类的接口,即便在实例化时也不会检查。为了避免运行时错误,虚拟子类要实现所需的全部方法

鸭子类型列子:发送验证码短信的列子

# 真正的鸭子类型:忽略对象的类型,转而关注对象有没有实现所需要的方法
class AliyunSms:
    def send(self):
        print(f"我用阿里云发短信,消息为")


class TencentSms:
    def send(self):
        print(f"我用腾讯云发短信")


class HuaweiSms:
    def send(self):
        print(f"我用华为云发短信")


def smsSend(sms):
    sms.send()


aliyun_sms = AliyunSms()
tencent_sms = TencentSms()
huawei_sms = HuaweiSms()
smsSend(aliyun_sms)
smsSend(tencent_sms)
smsSend(huawei_sms)
  • 1
  • 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

自定义抽象基类

# 从abc模块中,导出导入ABC, abstractmethod
from abc import ABC, abstractmethod


# 自定义抽象基类: 发短信的功能
# 自己定义的抽象基类要继承 abc.ABC
class SmsBase(ABC):
    # 抽象方法
    @abstractmethod
    def send(self):
        """
            使用@abstractmethod装饰器定义发短信的抽象方法
        """
        pass


class AliyunSms(SmsBase):
    def send(self):
        print(f"我用阿里云发短信")


class TencentSms(SmsBase):
    def send(self):
        print(f"我用腾讯云发短信")


class HuaweiSms(SmsBase):
    def send(self):
        print(f"我用华为云发短信")


def smsSend(sms):
    sms.send()


aliyun_sms = AliyunSms()
tencent_sms = TencentSms()
huawei_sms = HuaweiSms()
smsSend(aliyun_sms)
smsSend(tencent_sms)
smsSend(huawei_sms)

# issubclass(): 判断一个对象是否为另一个类或其子类(通过继承的子类、通过注册的虚拟子类)
print(issubclass(AliyunSms, SmsBase))
# __mro__魔术方法:方法解析顺序
# 结果为:(<class '__main__.AliyunSms'>, <class '__main__.SmsBase'>, <class 'abc.ABC'>, <class 'object'>)
print(AliyunSms.__mro__)
# 总结:通过继承来实现抽线基类的抽象方法固然可以,但是违背了Python 鸭子类型的初衷
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

总结:

通过继承来实现抽象基类的抽象方法固然可以,但是违背了Python 鸭子类型的初衷, 我们可以把一个类注册为抽象基类的虚拟子类,虚拟子类实现抽象基类的的全部抽象方法,同样达到继承的效果,也符合鸭子类型。

虚拟子类

# 从abc模块中,导出导入ABC, abstractmethod
from abc import ABC, abstractmethod, ABCMeta


# 自定义抽象基类: 发短信的功能
# 自己定义的抽象基类要继承 abc.ABC
class SmsBase(ABC):
    # 抽象方法
    @abstractmethod
    def send(self):
        """
            使用@abstractmethod装饰器定义发短信的抽象方法
        """
        pass


class AliyunSms:
    def send(self):
        print(f"我用阿里云发短信")


# issubclass(): 判断一个对象是否为另一个类或其子类(通过继承的子类、没有通过继承的虚拟子类)
# __mro__魔术方法:方法解析顺序
print(issubclass(AliyunSms, SmsBase))  # False 表面AliyunSms不是SmsBase的类或子类
print(AliyunSms.__mro__)

# AliyunSms类注册为SmsBase的子类
SmsBase.register(AliyunSms)
# 结果为:(<class '__main__.AliyunSms'>, <class 'object'>),说明注册的类不会从抽象基类中继承任何方法或属性
print(AliyunSms.__mro__)
print(issubclass(AliyunSms, SmsBase))  # True

sms = AliyunSms()
sms.send()
  • 1
  • 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

6.7 object类

object类是所有类的父类,默认所有的类都继承至Object类,因此所有类都有object类的属性和方法。

魔术方法含义
__new()__创建对象方法,创建对象时自动调用的方法,主要作用是创建对象,给该对象分配空间,方便之后的的操作。该方法会返回创建出来的对象实体,一旦正常的返回实体后,调用构造方法
__init()__构造方法,给对象的初始化设置
__str__()返回当前对象的字符串类型的信息描述,默认返回的是对象内存地址
__del__()删除该对象时会自动调用
__eq__()判断对象是否相等,当类没有重写__eq__()方法时,那么它也不应该重写__hash__()方法
__hash__()计算对象的哈希值
class Student:

    def __new__(cls, *args, **kwargs):
        print("创建对象")
        # 调用父类的创建对象的函数,进行当前对象的创建
        return super().__new__(cls)

    def __init__(self, name, age, sex):
        print("构造函数")
        self.name = name
        self.age = age
        self.sex = sex

    # 返回对象的字符串描述
    def __str__(self):
        return f"我是{self.name},今年{self.age}岁,是个{self.sex}的"

    def __del__(self):
        print("删除对象")


student1 = Student('张三', 20, '男')
student2 = Student('张三', 20, '男')
# 当输出对象时,输出该对象的字符串描述,自动调用__str__方法
print(student1)  # 我是张三,今年20岁,是个男的
# del stu1

# 默认返回对象的哈希值
print(student1.__hash__())  # 164948528721
print(student2.__hash__())  # 164948528733

# id()函数是获取对象的内存地址
print(id(student1))  # 2639176459536
print(id(student2))  # 2639176459728

print(student1 == student2)  # False
print(student1 is student2)  # False
  • 1
  • 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
  • 37

6.8 type、class、object的区别

在这里插入图片描述

type()函数的功能:返回实列话对象的class类型

"""
 Python 一切皆对象
 类和对象的区别:类可以新建实列化成对象,比如:a = str("abc"),这里的 a 叫实列化对象
 type、object、int、str、list、tuple、dict、set、自定义的类,都叫类,英文名字又叫 class,同时们也都是对象,对象都是具体的,是通过什么创建的呢?
 是通过 type 来实例化出来的,通常又叫做类对象
 type -> str -> abc
 最终总结:type -> 类对象(又叫class对象) -> 实列化对应类型的对象
"""
a = "abc"  # 等价于 a = str("abc")
# 说明 a 是 str 实例化出来的
print(type(a))  # <class 'str'>
# 说明 str 是 type 实例化出来的: str = type("str")
print(type(str))  # <class 'type'>
# 说明 object 是 type 实例化出来的
print(type(object))  # <class 'type'>
# 说明 type 是 type 自己实列化出来的
print(type(type))  # <class 'type'>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

6.9 == 和 is 的区别?

  1. 基本数据类型(数字、字符串、列表、元组、字典、集合), 比较两个对象的值是否相同is 比较两个对象的内存地址
  2. 自定义的类型,当我们使用 == 运算符比较两个对象时,Python 会自动调用__eq__()方法来进行比较,如果我们没有重写__eq__()方法,则 == 默认比较两个对象的内存地址,is也是比较两个对象的内存地址
class Student:

    def __new__(cls, *args, **kwargs):
        print("创建对象")
        # 调用父类的创建对象的函数,进行当前对象的创建
        return super().__new__(cls)

    def __init__(self, name, age, sex):
        print("构造函数")
        self.name = name
        self.age = age
        self.sex = sex

    # 返回对象的字符串描述
    def __str__(self):
        return f"我是{self.name},今年{self.age}岁,是个{self.sex}的"

    def __del__(self):
        print("删除对象")

    def __eq__(self, other):
        print("执行__eq__")
        if self.name == other.name and self.sex == other.sex:
            return True
        return False


student1 = Student('张三', 20, '男')
student2 = Student('张三', 20, '男')

# id()函数是获取对象的内存地址
print(id(student1))  # 2639176459536
print(id(student2))  # 2639176459728
print(student1 == student2)  # True
print(student1 is student2)  # False
  • 1
  • 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

6.10 常量与枚举

单值

# 定义性别常量两种方式:1男 2女 3未知
# 习惯常量均为大写,多个单词使用下划线连接
# 方式一:虽然是常量,但是是可变的
SEX_MAN = 1
SEX_WOMAN = 2
SEX_UNKNOW = 3

# 方式二:枚举
from enum import Enum, unique


@unique
class Sex(Enum):
    # 男
    MAN = "男"
    # 女
    WOMAN = "女"
    # 未知
    UNKNOW = (3, "未知")


# 访问枚举
print(Sex.MAN)  # Sex.MAN
print(Sex.MAN.name)  # MAN
print(Sex.MAN.value)  # 男


# 枚举不能修改
# Sex.MAN.value ="xxx"

# 枚举常常和模式匹配一起搭配使用
# 通过枚举获取信息
def sex_value(sex: Sex) -> int:
    match sex:
        case Sex.MAN:
            return Sex.MAN.value
        case Sex.WOMAN:
            return Sex.WOMAN.value
        case _:
            return Sex.UNKNOW.value


# 通过信息转成枚举
def sex_type(msg: str) -> Sex:
    match msg:
        case Sex.MAN.value:
            return Sex.MAN
        case Sex.WOMAN.value:
            return Sex.WOMAN
        case _:
            return Sex.UNKNOW


print(sex_value(Sex.WOMAN))  # 女
print(sex_type("女"))  # Sex.WOMAN
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

多个值(推荐:方便维护代码)

# 定义性别常量两种方式:1男 2女 3未知
# 习惯常量均为大写,多个单词使用下划线连接
# 枚举
from enum import Enum, unique


@unique
class Sex(Enum):
    # 1男
    MAN = (1, "男")
    # 2女
    WOMAN = (2, "女")
    # 3未知
    UNKNOW = (3, "未知")


# 枚举常常和模式匹配一起搭配使用
# 通过枚举获取信息
def sex_value(sex: Sex) -> int:
    match sex:
        case Sex.MAN:
            return Sex.MAN.value[0]
        case Sex.WOMAN:
            return Sex.WOMAN.value[0]
        case _:
            return Sex.UNKNOW.value[0]


# 通过信息转成枚举
def sex_type(msg: str) -> Sex:
    match msg:
        case Sex.MAN.value:
            return Sex.MAN
        case Sex.WOMAN.value:
            return Sex.WOMAN
        case _:
            return Sex.UNKNOW


# 访问枚举
print(Sex.MAN)  # Sex.MAN
print(Sex.MAN.name)  # MAN
print(Sex.MAN.value)  # (1, '男')
print(sex_value(Sex.WOMAN))  # 2
print(sex_type("女"))  # Sex.UNKNOW
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

6.11 类型注解

如下是常见的类型注解

类型描述
int整数
float浮点数
bool布尔
str字符串
bytes字节
object公共基类,任意对象
或Union
list/List列表: list1: list[str]、list2: List[str]
tuple/Tuple元组: tuple1: tuple[int]、tuple2: tuple[int, str]、tuple3: tuple[int, …]
Sequence序列:str、list、tuple、range都是常见的序列类型
dict/Dict/Maping字典:dict1: dict[str, int]
set/Set集合: set1: Set[int]
Iterable可迭代对象
Optional参数可以为空或已经声明的类型: opt1: str | None 等价于 opt2: Optional[str]
Any具有任意类型的动态类型值,与object是有区别的,未指定类型,都隐式地默认使用 Any
NoReturn函数没有返回结果,等价于None
Callable可调用类型, Callable[[参数类型], 返回类型]
NewType自定义创一个新类型: Age = NewType(“Age”, int)
from typing import List, Tuple, Dict, Set, Sequence, Optional, Union, NoReturn, Callable, NewType, Mapping, TypeVar

# 等价于 a = 1
a: int = 1
# 等价于 b = 3.1415926
a: float = 3.1415926
# 等价于 c = True
c: bool = True
# 等价于 d = "x"
d: str = "x"

# object,代表任意类型都可以
h: object = 1
h = 3.1415926
h = True
h = "x"

# 联合类型,可以是int类型或者str类型
# 方式一
f1: int | str = 123
# 方式二: 推荐方式
f2: Union[int, str] = 123

# 列表
# 定义一个list1,list1中元素放字符串
list1: list[str] = ["x1"]
# list2 和list等价
list2: List[str] = ["x1"]

list1.append("x2")
# list1.append(111111)
print(list1)

# 元组
# 定义固定大小的一元组,有且仅有1个元素,为int类型
tuple1: tuple[int] = (1,)
# 这种是错误写法
# tuple1: tuple[int] = (1, 2, 3)
# 定义固定大小的二元组,有且仅有2个元素,第一个是int类型,第二个是str类型
tuple2: tuple[int, str] = (1, "222")
# 定义可变大小的元组,可以放任意数量的元素,且类型为int类
tuple3: tuple[int, ...] = (1, 2, 3, 4)

# 序列
# str、list、tuple、range都是常见的序列类型
seq1: Sequence = "x"
seq2: Sequence = [1, "2", False]
seq3: Sequence = (1, "2", False)
seq4: Sequence = range(10)
# 这个就会提示:应为类型 'Sequence',但实际为 'int'
# seq5: Sequence = 11111

# 字典、映射
# key 为 str 类型,值为 int 类型
# 字典:dict 为 Mapping 的子类的子类的虚类
dict1: dict[str, int] = {"张三": 19}
# 映射
map1: Mapping[str, int] = {"张三": 19}


# 集合
# 集合中的元素均为int类型
set1: Set[int] = {1, 2, 3}

# 参数可以为空或已经声明的类型
# 方式一
opt1: str | None = "1111"
# 方式二
opt2: Optional[str] = "1111"


# 函数中使用
# 函数的两个参数,x 是 int 类型,y 是 int 类型,返回值是 int 类型
def num_sum(x: int, y: int) -> int:
    return x + y


# 函数没有返回结果
def print_info(x: Sequence) -> NoReturn:  # 等价 def print_info(x: Sequence) -> None
    print(x)


num_sum(1, 2)
# 没有返回值
print_info(["x", 1, 551.55])

# 类型别名: 可以将复杂一点类型给个别名,这样好用一些
# 别名
ConnAddress = tuple[str, int]
v1: ConnAddress = ("127.0.0.1", 8080)
# 等价写法
v2: tuple[str, int] = ("127.0.0.1", 8080)

# 可调用类型
"""
def num_sum(x: int, y: int) -> int:
    return x + y
"""
# 输入参数有两个参数,分别是 int、int,返回参数也是int
# Callable[..., int]: 表示输入的参数不限
制,输出的参数是 int
x: Callable[[int, int], int] = num_sum
# 执行
print(x(1, 3))
# 等价于
print(num_sum(1, 3))

"""
可以自定义创一个新类型: 主要用于类型检查
 Age = NewType("Age", int)
 1.NewType用来创建自定义数据类型;Age代表新类型;int代表原始类型。
 2.静态类型检查器会把新类型Age 看做原始类型int的一个子类,即 Age 其实也是 int 类型
"""
Age = NewType("Age", int)
# 提示:应为类型 'int',但实际为 'str'
# Age("xxx")
num = Age(18)
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

6.12 反射 getattr()

第7章 模块、包、异常

创建模块可以将模块中相关的代码(变量定义和函数定义等)编写在一个单独的文件中,并且将该文件命名为模块名+.py的形式,也就是说,创建模块,实际就是创建一个.py文件

  • 创建模块时,设置的模块名尽量不要与Python自带的标准模块名称相同

  • 模块文件的扩展名必须是 .py

7.1 导入模块

import modulename [as alias]
  • 1

参数说明:

  • modulename:要导入模块的名称

  • [as alias]:为模块起的别名

from modelname import member
  • 1

参数说明:

  • modelname:模块名称,区分字母大小写,需要和定义模块时设置的模块名称的大小写保持一致
  • member:用于指定要导入的变量、函数或者类等。可以同时导入多个定义,各个定义之间用逗号 “,” 隔开。如果想导入全部定义,也可以使用通配符星号 “**” 代替

7.2 包

使用模块可以避免函数名和变量名重名引发冲突。那么,如果模块名重复应该怎么办呢?在Python中,提出了包(Packaage)的概念。包括每一个分层次的目录结构,它将一组功能相近的模块组织在一个目录下。这样,既可以起到规范代码的作用,又能避免模块名重名引起冲突

包与目录的区别

  • 包含 __init__.py文件的目录称为包

  • 目录里通常不包含 __init__.py 文件

包的导入

import 包名.模块名
  • 1

7.3 异常

错误

  • 在编写代码时能够避免的
  • 语法错误:未按照代码限制进行编写,或者手误拼错了变量名等

异常

  • 编写代码阶段无法避免的

Python 中,提供了try语句捕获并处理异常

语句作用
trytryexcept前的语句将会由except来捕获
except语句后跟一个异常类(继承自BaseException或其子类),该except会捕捉该类或其子孙类实例。如果未捕获到且后续还有except语句则会由后续语句继续捕获,如果到最后一个except语句仍未被捕获,则抛出到调用者
else如果没有发生异常,执行else中的语句
finallyfinally语句中的代码,不管是否发生异常都会被执行

常见的语句如下:

try...except
try...except...else
try...except...except...else
try...except...else...finally
  • 1
  • 2
  • 3
  • 4
# 语句一
try:
    a = 1 / 0
except ZeroDivisionError:
    print("除法分母不能是0")

# 一句二
try:
    list1 = [1, 2, 3]
    list1[5]
except ZeroDivisionError:
    print("除法分母不能是0")
except IndexError as index:
    print(index)
    print("索引越界")

# 语句三
# try:
#     dict1 = {"zhangsan": 19}
#     dict1["lisi"]
# except ZeroDivisionError:
#     print("除法分母不能是0")
# except IndexError as index:
#     print(index)
#     print("索引越界")
# else:
#     print("错误")

# 语句四
try:
    dict1 = {"zhangsan": 19}
    dict1["lisi"]
except ZeroDivisionError:
    print("除法分母不能是0")
except IndexError as index:
    print(index)
    print("索引越界")
except Exception as ex:
    print(ex)
    print("出错了")

# 语句四
try:
    dict1 = {"zhangsan": 19}
    dict1["lisi"]
except ZeroDivisionError:
    print("除法分母不能是0")
except IndexError as index:
    print(index)
    print("索引越界")
except Exception as ex:
    print(ex)
    print("出错了")
finally:
    # 不管是否抛出异常,都会执行 finally 里面的代码块
    print("执行了 finally 里面的代码了!")
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

raise 抛出异常

class BaseSms(object):
    def send(self):
        # 子类没有重写改方法,则会调用父类的该方法,进而显示的抛出异常
        raise Exception("没有 send() 方法")


class AliyunSms(BaseSms):
    def send(self):
        print(f"我用阿里云发短信,消息为")


class TencentSms(BaseSms):
    pass


def smsSend(sms):
    sms.send()


smsSend(AliyunSms())
try:
    smsSend(TencentSms())
except Exception as ex:
    print(ex)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

自定义异常

在这里插入图片描述

BaseException是所有异常的基类,Exception是所有非退出异常的公共基类

class SmsException(Exception):
    def __int__(self):
        pass


class BaseSms(object):
    def send(self):
        # 子类没有重写改方法,则会调用父类的该方法,进而显示的抛出异常
        raise SmsException("没有 send() 方法")


class AliyunSms(BaseSms):
    def send(self):
        print(f"我用阿里云发短信,消息为")


class TencentSms(BaseSms):
    pass


def smsSend(sms):
    sms.send()


smsSend(AliyunSms())
try:
    smsSend(TencentSms())
except Exception as ex:
    print(ex)
  • 1
  • 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

第8章 文件及目录操作

8.1 创建和打开文件

Python中,想要操作文件需要先创建或者打开指定的文件并创建文件对象。这可以通过内置的open()函数实现。open()函数的基本语法格式如下:

file = open(filename[,mode[,buffering]])
  • 1

参数说明:

file:被创建的文件对象
filename:要创建或打开文件的文件名称,需要使用单引号或双引号括起来
mode:可选参数,用于指定文件的打开模式, 默认打开模式为只读(即 r)

r 只读只读模式打开文本文件,文件的指针放在文件开头,文件必须存在,否则出错,这也是打开文件的默认模式
r+ 读写读写模式打开文件,文件的指针放在文件开头,文件必须存在,否则出错,已可以写入新的内容覆盖原有的内容(从文件开头进行覆盖)
w 只写只写模式打开文件,文件存在,则将其覆盖,否则创建新文件
w+ 读写打开文件后,先清空原有的内容,使其变为一个空的文件
a只追加如果该文件已经存在,文件指针将放在文件的末尾,即新内容会被写入到已有内容之后。否则,创建新文件用于写入
a+ 追加和读取如果该文件已经存在,文件指针将放在文件的末尾,即新内容会被写入到已有内容之后。否则,创建新文件用于写入
"""
1.r 只读
  方式一
  读取文件 "a.txt" 中的数据,如果文件不存在,则报错,编码格式指定为 utf-8

  r+ 读写
  a.txt 的类容为 1234567
"""
file = open("a.txt", mode="r", encoding="utf-8")
# 读数据
print(file.read())  # 1234567
# 手动关闭流对象
file.close()

# 方式二(推荐): 使用 "with open() as 别名" 自动关闭流对象
with open("a.txt", mode="r", encoding="utf-8") as file:
    print(file.read())  # 1234567

# r+ 读写
with open("a.txt", mode="r+", encoding="utf-8") as file:
    # 先读
    # print(file.read())  # 1234567,此时指针在7的位置后面
    # 后写
    # file.write("xxx")  # 写的时候,指针在7的位置后面,因此写入的数据变为了"1234567xxx"

    # 先写
    file.write("xxx")  # 此时指针在开头位置,因此 123被覆盖成了xxx,文件的结果为xxx4567,指针的位置指向4
    # 后读
    print(file.read())  # 4567,此时指针在7的位置后面

    # 将文件指针移动到文件开头
    file.seek(0)
    # 并读取文件内容
    print(file.read())  # xxx4567

"""
2.w 只写
  向文件 "a.txt" 中写入数据,如果文件不存在则新建,否则覆盖,写入的编码格式为 utf-8
  w+ 读写
"""
with open("b.txt", mode="w", encoding="utf-8") as file:
    file.write("hello 我是李四!")

# w+ 读写
with open("c.txt", mode="w+", encoding="utf-8") as file:
    file.write('Hello, world!')
    # 将文件指针移动到文件开头
    file.seek(0)
    # 并读取文件内容
    print(file.read())  # Hello, world!

"""
3.a 追加
  向文件 "c.txt" 追加数据,
  如果该文件已经存在,文件指针将放在文件的末尾,即新内容会被写入到已有内容之后。
  否则,创建新文件用于写入,写入的编码格式为 utf-8
"""
with open("d.txt", mode="a+", encoding="utf-8") as file:
    file.write('Hello, world!')
    # 将文件指针移动到文件开头
    file.seek(0)
    # 并读取文件内容
    print(file.read())  # Hello, world!
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

8.2 读取文件

方法含义
read([size])size可选参数,指定读取的字符个数,如果省略,则一次性读取所有内容
readline()每次读取一行
readlines()读取整个文件所有行,保存在一个list
seek(offset)将文件的指针移动到新的位置
"""
文件 "e.txt" 中的内容为
    春眠不觉晓
    处处闻啼鸟
    夜来风雨声
    花落知多少
"""
with open("e.txt", mode="r", encoding="utf-8") as file:
    # 一次性读取所有内容
    print(file.read())

    # 将文件指针移动到文件开头
    file.seek(0)
    # 一次读取一行内容
    print(file.readline())  # 春眠不觉晓
    
    # 将文件指针移动到文件开头
    file.seek(0)
    # 读取整个文件所有行,保存在一个list中
    print(file.readlines())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

春眠不觉晓
处处闻啼鸟
夜来风雨声
花落知多少
春眠不觉晓

[‘春眠不觉晓\n’, ‘处处闻啼鸟\n’, ‘夜来风雨声\n’, ‘花落知多少’]

8.3 目录操作

在 Python 中,内置的 os 模块和 os.path 模块用于对目录和文件进行操作

os 模块相关

方法描述
getcwd()获取当前工作目录的路径
listdir(path)列出指定路径 path 下的文件和文件夹的列表。
mkdir(path)创建新目录 path
makedirs(path)递归创建多层目录,如果该目录已存在则抛出异常,注意:“D:\a\b”" 和 "D:\a\c"并不会冲突
remove(path)删除指定路径 path 下的文件
rmdir(path)删除空文件夹,删除的文件夹非空会提示错误
removedirs(path)删除多级目录
rename(old, new)将文件 old 重命名为 new
chdir(path)切换工作路径
access(path,accessmode)获取对文件是否有指定的访问权限(读、写、执行),accessmode的是R_OK(读取)、W_OK(写入)、X_OK(执行)、F_OK(文件是否存)
chmod(path,mode)修改path的访问权限

os.path 模块相关

方法描述
abspath(path)获取文件或目录的绝对路径
exists(path)判断文件或目录是否存在,如果存在返回True,否则返回False
join(path,name)将目录与目录、目录与文件名拼接起来
isdir()判断指定路径 path 是否是一个目录
isfile(path)判断指定路径 path 是否是一个文件
split()分离目录和文件名
splitext()分离文件名和扩展名
basename(path)从一个目录中提取文件名
dirname(path)从一个目录中提取文件的父路径

第9章 pip、anaconda 和 virtualenv

pip、anaconda 和 virtualenv 都提供了对 Python 包的查找、下载、安装、卸载的功能。

  • pip 已内置于 Python 3.4 和 2.7 及以上版本,其他版本需另行安装
  • virtualenv是一个环境管理工具,使用 virtualenv 可以创建一个完全隔离的环境,但 virtualenv 只能创建基于本机已存在的 python 版本的虚拟环境;使用 virtualenv 创建完成环境以后,推荐使用 pip 安装 python 包
  • conda 结合了pip 和 virtualenv 两者的功能,使用 conda 可以创建任意 python 版本的虚拟隔离环境,而且 conda 还是一个包管理工具,不但可以安装 python 包,而且可以安装其他语言的包,更重要的是 conda 具有完美的包依赖关系处理能力,可以轻松的安装所需的包,而不用手动处理各种包之间的依赖关系;
  • pip、anaconda 和 virtualenv 默认下载包的地址都是国外,速度会很慢,因此需要配置国内的加速源
  • 学习、项目开发中 强烈推荐 anaconda 隔离环境, 使用 pip + conda 下载依赖包(有时候 conda 仓库中没有对应版本的包,则可以切换到 conda 对应的环境 的 pip 来下载包)

在这里插入图片描述

9.1 配置国内加速源

9.1.1 配置 pip 加速源

国内常用的 pip 镜像源:

  • 清华:https://pypi.tuna.tsinghua.edu.cn/simple (官方认可的源)

  • 阿里云:http://mirrors.aliyun.com/pypi/simple/

  • 中国科技大学: https://pypi.mirrors.ustc.edu.cn/simple/

  • 豆瓣:http://pypi.douban.com/simple/

修改 pip 源,主要有 2 种方式:(参考的pip v23.2.1 版本文档)

  • 每次下载包的时候,手动指定 pip 源

    这里举了个例子:从清华的 pip 源下载 pandas 包

    # 不区分 pandas 和 -i 的位置
    # 好处: 不必修改本地的 pip 配置文件
    # 坏处: 每次都要在 pip install 后面添加老长的源地址
    pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 修改本地的 pip 配置文件

    pip 有 3 个“级别”的配置文件:

    • global:系统范围的配置文件,跨用户共享
    • user:每个用户的配置文件
    • site:每个环境的配置文件;即每个虚拟环境

    加载顺序: global -> user -> site

    Windows(学习、开发环境

    • global: C:\ProgramData\pip\pip.in
    • user: %APPDATA%\pip\pip.ini (推荐、默认方式, 实际指的是 C:\Users\用户名\AppData\Roaming\\pip\pip.ini ); 旧方式: %HOME%\pip\pip.ini (不推荐)
    • site:指定虚拟环境(anaconda 和 virtualenv)/pip.ini (不推荐)

    注意:

    ​ %APPDATA% 含义是:获取环境变量”APPDATA”的目录,可以通过 cmd 执行 echo %APPDATA% 命令查目录

    ​ %APPDATA% 指的是 C:\Users\用户名\AppData\Roaming 目录,并不是 C:\Users\用户名\AppData 目录

    Mac(学习、开发环境)

    • global: /Library/Application Support/pip/pip.conf
    • user: 如果$HOME/Library/Application Support/pip 目录存在,则是 $HOME/Library/Application Support/pip/pip.conf,否则是 $HOME/.config/pip/pip.conf (推荐); 旧方式: $HOME/.pip/pip.conf (不推荐)
    • site:指定虚拟环境(anaconda 和 virtualenv)/pip.conf (不推荐)

    Linux(生产环境)

    • global: /etc/pip.conf (推荐)
    • user: $HOME/.pip/pip.conf
    • site:指定虚拟环境(anaconda 和 virtualenv)/pip.conf (不推荐)

配置文件 pip.ini/pip.conf 中的内容

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
timeout = 10
[install]
trusted-host = pypi.tuna.tsinghua.edu.cn
  • 1
  • 2
  • 3
  • 4
  • 5

index-url = https://pypi.tuna.tsinghua.edu.cn/simple 含义: 设置加速源为清华源

timeout = 10 含义: http 连接超时设置10秒

trusted-host = pypi.tuna.tsinghua.edu.cn 含义: 信任的镜像源的域名或主机,否则会有告警

9.1.2 配置 anaconda 加速源

anaconda 的配置文件”.condarc“的位置

windows: C:\Users\用户名\.condarc

配置文件 ”.condarc“ 的内容

channels:
  - defaults
show_channel_urls: true
channel_alias: https://anaconda.mirrors.sjtug.sjtu.edu.cn/
default_channels:
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/main
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/free
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/mro
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/msys2
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/pro
  - https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/r
custom_channels:
  conda-forge: https://anaconda.mirrors.sjtug.sjtu.edu.cn/conda-forge
  soumith: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/soumith
  bioconda: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/bioconda
  menpo: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/menpo
  viscid-hub: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/viscid-hub
  atztogo: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/atztogo
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

9.1.3 配置 virtualenv 加速源

virtualenv 使用 python 自带的 pip 安装依赖

9.2 命令大全

常用命令总结

意义pipconda
查看版本pip --versionconda --version
查看已安装的依赖清单pip listconda list
在当前环境中安装指定版本的包pip install 包名==版本号conda install 包名==版本号
删除当前环境中的包pip uninstall 包名conda remove 包名
创建环境conda create -n 环境名 python=版本号
激活环境activate 环境名

9.3 虚拟环境的复制和迁移

方法一 :requirements.txt

方法二 : environment.yml

requirements.txt 与 environment.yml

它们的目的都是为了帮助别人在别处恢复出相同的环境,所以是先构建好了项目环境后,再生成,用来记录、恢复环境所需的依赖环境

9.3.1 requirements.txt

这个方法不推荐,因为只会导出你使用 pip 安装的依赖包,不会导出 conda 虚拟环境安装的依赖包,并不适用于虚拟环境的迁移的应用场景。

pip install numpy

conda install Django

# 生成 requirements.txt
pip freeze > requirements.txt

# 从 requirements.txt 安装依赖
pip install -r requirements.txt 
  • 1
  • 2
  • 3
  • 4
  • 5

9.3.2 environment.yml

environment.yml 是用 conda 命令将环境信息导出备份的文件。

本教程使用的 conda 版本是 conda 23.5.2

# 生成 environment.yml
conda env export > environment.yml

# 通过 environment.yml 创建新虚拟环境,并下载对应的依赖
conda env create -f environment.yml

# 通过 environment.yml 创建新虚拟环境,覆盖 environment.yml 中的虚拟环境名,并下载对应的依赖
conda env create -f environment.yml -n 新虚拟环境名
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
name: xxx
channels:
  - defaults
dependencies:
  - asgiref=3.5.2=py38haa95532_0
  - backports=1.1=pyhd3eb1b0_0
  - backports.zoneinfo=0.2.1=py38h2bbff1b_0
  - ca-certificates=2023.05.30=haa95532_0
  - django=4.1=py38haa95532_0
  - libffi=3.4.4=hd77b12b_0
  - openssl=3.0.9=h2bbff1b_0
  - pip=23.2.1=py38haa95532_0
  - python=3.8.17=h1aa4202_0
  - python-tzdata=2023.3=pyhd3eb1b0_0
  - setuptools=68.0.0=py38haa95532_0
  - sqlite=3.41.2=h2bbff1b_0
  - sqlparse=0.4.4=py38haa95532_0
  - tzdata=2023c=h04d1e81_0
  - vc=14.2=h21ff451_1
  - vs2015_runtime=14.27.29016=h5e58377_2
  - wheel=0.38.4=py38haa95532_0
  - pip:
      - numpy==1.24.4
prefix: D:\python\anaconda3\envs\xxx
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

上面所示的 environment.yml

  • numpy 通过 pip 下载

  • django 通过 conda 下载

  • django=4.1=py38haa95532_0 表示的是 D:\python\anaconda3\envs\xx1\conda-meta\django-4.1-py38haa95532_0.json

简而言之,conda 的 environment.yml 提供的信息比 pip 的 requirements.txt 提供的信息更完整,推荐优先选择 environment.yml

第10章 json

10.1 json 是什么

JSON(JavaScript Object Notation) 是一种轻量级的、基于文本的、开放的数据交换格式,易于人阅读和编写。JSON 在 Web 开发领域有着举足轻重的地位,必须熟悉 JSON。

10.2 json 的数据类型

json 的数据类型:

  • number(数值,包括整数或浮点数): 0、3.14、17

  • string(字符串,必须使用双引号): “张三”

  • 布尔值: true 或 false

  • array(数组,用中括号 []表示): [1,2,3]、 [“打游戏”, “打篮球”, “爬山”]

  • Object(对象): key-value 键值对的集合,使用花括号{ }定义,多个键-值对之间使用逗号,分隔

    {
    	"username": "张三",
    	"age": 17,
    	"like": ["打游戏", "打篮球", "爬山"],
    	"is_adult": false,
    	"xxx": null
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • null(空)

python 原始类型与 json 类型相互转化对照表:

jsonpython解释
numberint, float0、3.14、17
stringstr“张三”
trueTrue布尔 True
falseFalse布尔 False
arraylist,tuple[1,2,3]、[“打游戏”, “打篮球”, “爬山”]
objectdict{
“username”: “张三”,
“age”: 17,
“like”: [“打游戏”, “打篮球”, “爬山”],
“is_adult”: false,
“phone”: null
}
nullNone

在使用 json 时,有以下几点需要注意:

  • json 是一段包裹在花括号{}中的数据,数据由若干key-value 键值对组成

  • key-value 键值对中的 key 必须是字符串,value 可以是 json 中的任意类型(数字、字符串、布尔值、数组、对象、null;)

  • 键必须是唯一的,不能重复,否则后定义的键-值对会覆盖前面定义的键-值对

10.3 python 中的 josn 库

python 在使用 json 这个模块前,首先要导入 json 库:import json

方法描述
json.dumps()将 python 对象编码成 json 字符串
json.loads()将已编码的 json 字符串解码为 python 对象
json.dump()将 python 内置类型序列化为 json 对象后写入文件
json.load()读取文件中 json 字符串转化为 python 的字典类型

注意:不带s的是序列化到文件或者从文件反序列化,带s的都是内存操作不涉及持久化

import json

# python 整数、浮点转换为 json 的数字
print(json.dumps(1))  # 1
print(json.dumps(3.14))  # 3.14

# python 字符串转换为 json 的字符串
print(json.dumps('abc'))  # 3.14

# python True/False 转换为 json true/false
print(json.dumps(True))  # true
print(json.dumps(False))  # false

# python 列表、元组转换为 json 的数组
print(json.dumps([1, 2, 3]))  # [1, 2, 3]
print(json.dumps((4, 5, 6)))  # [1, 2, 3]

# 重点(以后基本使用的是该方式): python 字典转换为 json 对象
# 结果:{"username": "\u5f20\u4e09", "age": 17, "like": ["\u6253\u6e38\u620f", "\u6253\u7bee\u7403", "\u722c\u5c71"], "is_adult": false, "phone": null}
data = {"username": "张三", "age": 17, "like": ["打游戏", "打篮球", "爬山"], "is_adult": False, "phone": None}
result = json.dumps(data)
print(result)
# 将已编码的 json 字符串解码为 python 对象
print(json.loads(result))

with open("test.json", "w", encoding="utf-8") as f:
    # 将 python 内置类型序列化为 json 对象后写入文件
    # 其中 indent=4 表示4个缩进单位
    json.dump(data, f, indent=4)


with open("test.json", "r", encoding="utf-8") as f:
    # 读取文件中 json 字符串转化为 python  的字典类型
    load = json.load(f)
    print(type(load))  # <class 'dict'>
    # {'username': '张三', 'age': 17, 'like': ['打游戏', '打篮球', '爬山'], 'is_adult': False, 'phone': None}
    print(load)
  • 1
  • 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
  • 37

10.4 自定义类型转 json

们在 python 的json.JSONEncoder 类中,可以查看 python 数据序列化为 json 格式的数据时数据类型的对应关系。

在使用 json.dumps() 时,可能会遇到 TypeError: Object of type xxx is not JSON serializable 错误,表示无法序列化某些对象格式,比如自定义对象、datetime日期格式等。

json 库默认支持的类型:int, float、str、list、tuple、dict、bool,其他的类型都需要自定义 jsonEncoder。

import json

"""
  json.dumps()	将 python 对象编码成 json 字符串       
  json.loads()	将已编码的 json 字符串解码为 python 对象   
  json.dump() 	将 python 内置类型序列化为 json 对象后写入文件
  json.load() 	读取文件中 json 形式的字符串元素转化为 json 类型
"""


def parse_json(context: str) -> object:
    """
     json 字符串转成 python 对象
    :return:
    """
    return json.loads(str)


class Student:

    def __init__(self, username: str, age: int, like: list, is_adult: bool, phone: str = None):
        self.username = username
        self.age = age
        self.like = like
        self.is_adult = is_adult
        self.phone = phone

    def json_format(self) -> dict:
        """
         方式一
        :return:
        """
        # 会抱错 Object of type Student is not JSON serializable
        # return json.dumps(self)
        data = {
            "username": self.username,
            "age": self.age,
            "like": self.like,
            "is_adult": self.is_adult,
            "phone": self.phone
        }
        return data


student = Student(username="张三", age=17, like=["打游戏", "打篮球", "爬山"], is_adult=False)
print(json.dumps(student.json_format()))
  • 1
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/533227
推荐阅读
相关标签
  

闽ICP备14008679号