赞
踩
阅读说明:
该笔记是本人《抖音:程序员头牌 | vx: cd20242024》经历三个月打磨编写的,其中参考阅读的书籍为《流程的python》。本笔记加入了工作中遇到的一些问题和对应的见解
有写得不对得、需要添加的知识点,可以在评论区指出,本人会对相应的进行修改和添加
有pdf版本,需要可以@我,记得一键三联哟!禁止转载!
学习必读:
本教程主要针对大学学生准备的《零基础学 Python》 的学习教程
课程适合的专业:大学所有专业均可学习,本课程考虑到了非计算机专业的学生,会补充一些非常基础的知识,如果你已知道该小节知识点,可以跳过该小节
学习习惯:看一小节视频、写一小节的代码,养成复习的习惯,晚上睡觉前复习一下白天学习的知识点,多动手练习
学习该课程,尽量严格按照老师的环境要求、搭建流程来搭建环境,减少学习途中遇到的各种环境问题
指标 | 参数 | 解释 |
---|---|---|
电脑系统 | win11 | win10、win11 都是可以的 |
Python | Python 3.11 | Anaconda 控制版本 |
Anaconda | Anaconda3-2023.07-1-Windows-x86_64 | 下载连接:官方 | 国内加速 |
强烈安装的软件:
3.3浏览器:Google Chrome 网络浏览器
文本编辑工具:
Notepad ++
markdown 编辑器:
typora
截图 + 贴图工具:
snipaste
中国风的作图工具(谷歌浏览插件):
Excalidraw
画流程工具: drawio
Anaconda:
是一个开源的 Python 发行版本,其包含了 conda、Python 等180多个科学包及其依赖项, conda 是一个开源的包、环境管理器,可以用于在同一个机器上安装不同版本的软件包及其依赖,并能够在不同的环境之间切换
注意
: 本教程使用 Anaconda 控制 Python 版本,不需要单独搭建 Python 环境,Anaconda 自带 Python
Pycharm:
PyCharm 是一款 Python 的代码编辑器, 功能强大,学习/企业开发 Python 项目均使用此软件
已经安装 Python 和 Pycharm, 再装 Anaconda 需不需要卸载原有的Python?
根据老师使用经验和教学经验中遇到的问题来讲,一般不直接安装 Python, 使用 Anaconda 环境管理,自带一个 base环境,强烈推荐卸载已经安装的 Python,卸载干净以后,再安装 Anaconda,可以避免很多问题,而 Pycharm 不用重新安装!
已经安装 Anaconda了,但是版本和老师的不一样,需要卸载重新安装吗?
如果 Anaconda 支持 Python 3.11 ,推荐不用重新安装,如果不支持 Python 3.11,说明版本太老了,则推荐卸载重新安装。因为本教程是基于 Python 3.11录制的,有新特性,低版本的无法使用新特性的功能
双击 Anaconda3-2023.03-Windows-x86_64.exe
运行,跟着下面图文教程安装即可:
勾选
Just Me
,表示 仅当前登录的用户使用,再点击Next
下一步Anaconda 默认安装路径是 C 盘,由于 C 盘是系统盘,Anaconda 占的硬盘内存比较大,容易导致 C 盘的硬盘内存不足,因此修改默认安装路径,修改 Anaconda 安装路径为:D:\Python\anaconda3 ;修改安装路径后,点击
Next
下一步勾选前三个选项。点击
Install
,之后等待安装安装完成后,点击
Next
之后,Anaconda 安装就完成了!
因为 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
运行
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
阿里云源
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
北京外国语大学源
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
双击 pycharm-professional-2023.1.exe
运行,跟着下面图文教程安装即可:
- 点击
Next
,下一步- 修改 Pycharm 安装路径为:D:\Python\PyCharm 2023.1 ,再点击
Next
,下一步- 勾选所有选项,点击
Next
,下一步- 点击
Install
,之后等待安装- 安装完成后,点击
Finish
,安装就完成了
utf8
编码至此,所有软件安装完成了!
注释就是对代码的解释和说明,让人了解代码实现的具体功能,从而帮助程序员更好地阅读代码,在 Python 中,包含三种类型的注释
单行注释
# 这是注释内容
多行注释
'''
这是单引号注释内容
'''
"""
这是双引号注释内容
"""
文件编码声明注释
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 *-*
或者
# coding:utf8
技巧:可以在 Pycharm 中全局设置编码方式为 utf-8
Python
不像其他编程语言,如 C
或 Java
采用大括号 {}
分隔代码块,而是采用冒号 :
和代码缩进 区分代码之间的层次
缩进可以使用空格或者
<Tab>
键实现,其中,使用空格时,通常采用4
个空格作为一个
缩进单位,使用<Tab>
键时,采用一个<Tab>
键作为一个
缩进单位,在 Pycharm 中通常建议采用<Tab>
键进行缩进
# 定义年龄
age = 17
if age < 18:
print("未成年")
Python
中采用 PEP 8
作为编码规范,Pycharm
软件会自动对代码进行 PEP 8
编码校验,如果不符合代码规范的,代码下边会有波浪线提示!常见规则如下:
Pycharm
中PEP 8
规范格式化快捷键:Ctrl + alt + L
不要在代码结尾添加 ;
建议每行代码不要超过 80
个字符,如果超过,建议使用小括号 ()
连接起来
使用必要的空行
可以增加代码的可读性
通过情况下,运算符两侧、函数参数之间,,
两侧,建议使用空格进行隔离
模块名尽量短小,并且全部使用小写字母,可以使用下划线分隔多个字母
包名尽量短小,并且全部使用小写字母,包之间强烈推荐使用 "."
连接 不推荐使用下划线,列如:com.baidu.Python
类名采用大驼峰命名,每个单词首字母大写,其他字母小写
模块内部的类采用下划线"_" + 类名
函数、类的属性和方法的命名规则同模块的类似,也是全部小写字母,多个字母间用下划线_
分隔
常量命名时采用全部大写字母,可以使用下划线
使用单下划线_
开头的模块变量或者函数是受保护的,在使用from xxx import *
语句从模块中导入时这些变量或函数不能被导入
使用双下划线__
开头的实例变量或方法是类私有的
保留字(关键字)是 Python
语言中一些已经被赋予特定意义的单词,开发程序时,不可以把这些保留字作为变量、函数、类、模板和其他对象的名称来使用。常见的保留字表如下:
and | as | assert | break | class | continue |
---|---|---|---|---|---|
def | del | elif | else | except | finally |
for | from | False | global | if | import |
in | is | lambda | nonlocal | not | None |
or | pass | raise | return | try | True |
while | with | yield |
Python
中所有保留字是区分字母大小写的,列如and
是保留字,但是And
就不属于保留字, 其中Pycharm
软件中会自动检测标识符是否是保留字
,是保留字,则编译不过
标识符可以简单的理解为一个名字,比如每个人都有自己的名字,它主要是来标识变量名、函数命、类名、模块和其他对象的名称。
Python 语言标识符命名规则如下:
由字母(a-z,A_Z)
、数字
和下划线
"_"
组成,第一个字符不能是数字
不能使用 Python
保留字(关键字)
严格区分字母大小写
案列:
合法的标识符
USER_ID # 大写字母 + 下划线
username # 全小写字母
User_age123 # 大写字母 + 小写字母 + 下划线 + 数字
user123 # 小写字母 + 数字
_age # 下划线 + 小写字母
__name # 下划线 + 小写字母
不合法的标识符
4user # 第一个字符不能是数字
and # and 为 Python 中的保留字
$user # 不能使用特殊符号,符号只能使用下划线 "_"
严格区分大小写
# age 和 Age 是两个不同的标识符
age
Age
变量严格意义上应该称为 名字
,你的快递存放在货物架上,上面贴着写有你名字的标签。当你来取快递时,并不需要知道它们存放在这个大型货架的具体位置,只需要提供你的名字,快递员就会把你的快递交给你。实际上,变量也一样,你不需要知道信息存储在内存中的准确位置,只需要记住存储变量时所用的名字,再调用这个名字就可以了
变量名本质就是一个有效的标识符。
使用变量的一些规则和经验:
变量名必须是一个有效的标识符
变量名不能使用 Python
中的保留字(关键字)
应该选择有意义的单词作为变量名,比如年龄 age
慎用小写字母l
和大写字母O
,因为小写字母l
和数字1
肉眼看起来特别相似,同样大写字母O
和数字0
肉眼看起来特别相似,记住这条经验可以减少开发中遇到该项问题
为变量赋值,可以通过等号 "="
来实现,语法格式为:
变量名 = 变量值
案列
# 数字1024:创建变量名为 number,并赋值为 1024,该变量类型为 整数类型
number = 1024
# 姓名 张三:创建变量名为 username,并赋值为 张三,该变量类型为 字符串类型
username = "张三"
# 使用 内置函数 type() 可返回变量类型
# 输出: number 变量 类型为: <class 'int'>
print("number 变量 类型为:", type(number))
# 输出: username 变量类型为: <class 'str'>
print("username 变量类型为:", type(username))
Python 是一种动态类型的语言,也就说,变量的类型可以随时变化
# 创建变量名为 username,并赋值为 张三,该变量类型为 字符串类型
username = "张三"
# 输出: username 变量 类型为: <class 'str'>
print("username 变量 类型为:", type(username))
username = 1024
# 输出: username 变量类型为: <class 'int'>
print("username 变量类型为:", type(username))
使用 内置函数 type() 可返回变量类型
在 Python
语言中,数字类型主要包含:整数、浮点数、复数、布尔
数学中,整数包括正整数
、负整数
和0
,案列:
number1 = 1
print(number1) # 1
number2 = -35
print(number2) # -35
number3 = 0
print(number3) # 0
浮点数(对小数的近似表示)由整数部分和小数部分组成,列如:3.1415926
,0.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
由于工作中很少用到,这里就不讲解了
布尔类型主要用于表示真值或假值, 在 Python
中 标识符 True
和 False
被解释为布尔值,另外布尔值可以转化为数值,即True
代表整数1
,False
代表整数0
在
Python
中 只有如下几种情况得到的值为假
False
或None
数值中的零:包括
0
、0.0
空序列:包括
空字符("")
、空列表([])
、空字典({})
、空元组(())
总结:
非空即真,非零即真
这里先不给大家讲解,讲到流程控制语句再详细讲解
案列
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
字符串就是连续的字符序列,在 Python
中,通常使用单引号''
、双引号""
、三引号'''或"""
表示
案列
# 单引号 name1 = '张三' print(name1) # 张三 # 双引号 name2 = "张三" print(name2) # 张三 # 三单引号 name3 = ''' 我是张三,今年18岁,我 今年刚考入大学读书 ''' print(name3) # 三双引号 name4 = """ 我是张三,今年18岁,我 今年刚考入大学读书 """ print(name4)
Python 3.6
引入了一种新的字符串格式化方式:f-string
格式化字符串, 这种格式化的方式越来越直观,以后有且仅推荐使用该方式
x = 2
y = 3
# 在字符串最前面 输入小写字母
a = f"x = {x}, y = {y}"
print(a) # x = 2, y = 3
后面讲序列化的时候,再详细讲解其他功能
虽然 Python
是弱类型编程语言,不需要像 Java
或 C
语言那样还要在使用变量前声明变量的类型,但在一些特定场景中,仍然需要用到类型转换。下表是 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
算术运算符就是数学运算符,比如加减乘除
下表列出了 Python
支持常见的基本算术运算符
运算符 | 说明 | 实例 | 结果 |
---|---|---|---|
+ | 加法 | 10.5 + 10 | 20.5 |
- | 减法 | 4.5 - 0.25 | 4.25 |
* | 乘法 | 4 * 2.5 | 10.0 |
/ | 除法 | 5 / 2 | 2.5 |
// | 整除:只保留商的整数部分 | 5 // 2 | 2 |
% | 取余:返回除法的余数 | 5 % 2 | 1 |
** | 幂运算:返回 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
某学员的课程成绩如下:
课程 | 分数 |
---|---|
C 语言 | 89 |
Python | 84 |
mysql | 91 |
求三门课程的平均分?
# 定义变量,存储 c 语言课程的分数
c = 89
# 定义变量,存储 Python课程的分数
Python = 84
# 定义变量,存储 mysql 课程的分数
mysql = 93
# 求平均成绩
avg = (c + Python + mysql) / 3
# 三门课程的平均分为:88.66666666666667分
print(f"三门课程的平均分为:{avg}分")
赋值运算符用来把右侧的值传递给左侧的变量,可以直接将右侧的值交给左侧的变量,也可以右侧进行某些运算后再交给左侧的变量,比如加减乘除、函数调用、逻辑运算等。
运算符 | 说 明 | 用法举例 | 等价形式 |
---|---|---|---|
= | 赋值运算 | x = y | x = y |
+= | 加赋值 | x += y | x = x + y |
-= | 减赋值 | x -= y | x = x - y |
*= | 乘赋值 | x *= y | x = x * y |
/= | 除赋值 | x /= y | x = x / y |
%= | 取余数赋值 | x %= y | x = x % y |
**= | 幂赋值 | x **= y | x = x ** y |
//= | 取整数赋值 | x //= y | x = 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
关系运算符,也称比较运算符,用于对常量、变量或表达式的结果进行大小比较。如果比较是成立的,则返回 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
数学中我们就学过逻辑运算,例如 p
为真命题,q
为假命题,那么p且q
为假,p或q
为真,非q
为真等等
逻辑运算符 | 含义 | 基本格式 | 说明 |
---|---|---|---|
and | 逻辑 “与” 运算,等价于数学中的“且” | a and b | 当 a 和b 两个表达式都为真时,a and b 的结果才为真,否则为假。 |
or | 逻辑 “或” 运算,等价于数学中的“或” | a or b | 当 a 和 b 两个表达式都为假时,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
Python
位运算按照数据在内存中的二进制位进行操作,它一般用于底层开发,在应用层开发中并不常见。同时涉及到的知识点比较多,我们先跳过本小节,以后需要的话再来学习
所谓优先级,就是当多个运算符同时出现在一个表达式中时,先执行哪个运算符,与数学的四则运算 先乘除,后加减,有括号时要先算括号内的
是一个道理
Python中运算符的运算规则是:优先级高的运算符先执行,优先级低的运算符后执行,同一优先级的操作按照从左到右的顺序进行
常用优先级运算规则表如下:
小技巧
在编写程序的时候,尽量使用括号
"()"
来限定运算符的次序,避免运算符次序发生错误
运算符说明 | Python运算符 | 优先级 |
---|---|---|
小括号 | ( ) | 19 |
索引运算符 | x[i] 或 x[i1: i2 [:i3]] | 18 |
属性访问 | x.attribute | 17 |
乘方 | ** | 16 |
按位取反 | ~ | 15 |
符号运算符 | +(正号)、-(负号) | 14 |
乘除 | * 、/ 、// 、% | 13 |
加减 | + 、- | 12 |
位移 | >>、<< | 11 |
按位与 | & | 10 |
按位异或 | ^ | 9 |
按位或 | | | 8 |
比较运算符 | == 、!= 、> 、>= 、< 、<= | 7 |
is 运算符 | is、is not | 6 |
in 运算符 | in、not in | 5 |
逻辑非 | not | 4 |
逻辑与 | and | 3 |
逻辑或 | or | 2 |
逗号运算符 | exp1, exp2 | 1 |
input()
是 Python
的内置函数,用于从控制台读取用户输入的内容。input()
函数总是以字符串的形式来处理用户输入的内容,所以用户输入的内容可以包含任何字符,其中注意按下回车键后 input() 读取就结束了
input()
函数的用法为:
str = input(tipmsg)
说明:
str
表示一个字符串类型的变量,input
会将读取到的字符串放入str
中。tipmsg
表示提示信息,它会显示在控制台上,告诉用户应该输入什么样的内容;如果不写tipmsg
,就不会有任何提示信息
实战:
题目:请在控制台输入你的
Python
、C
语言、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}")
前面使用 print()
函数时,都只输出了一个变量,但实际上 print()
函数完全可以同时输出多个变量,而且它具有更多丰富的功能
和其它编程语言一样,按照执行流程划分,Python
程序也可分为 3 大结构,即顺序结构
、选择结构
和循环结构
:
顺序结构: 程序从上到下逐行地执行
选择结构: 根据条件,选择性地执行某段代码
循环结构: 根据循环条件,重复性的执行某段代码
# 语法格式
if 表达式:
语句块
如果表达式为真
,则执行 语句块
;表达式如果为假
,就跳过 语句块
,继续执行后面的语句,这种形式的if
语句,相当于汉语里的关联词语 如果......, 就......
需求:控制台输入你的年龄,如果该同志的年龄小于 18 岁,则输出“青少年”
age = int(input("请输入年你的年龄:"))
if age < 18:
print("青少年")
# 语法格式
if 表达式:
语句块1
else:
语句块2
需求:控制台输入你的年龄,如果该同志的年龄小于 18 岁,则输出“青少年”,否则输出 “已成年”
age = int(input("请输入年你的年龄:"))
if age < 18:
print("青少年")
else:
print("已成年")
# 语法格式
if 表达式1:
语句块1
elif 表达式2:
语句块2
elif 表达式3:
语句块3
......
......
......
else:
语句块n
需求:编写程序根据分数打印结果,90分及以上为优秀,80分及以上为良好,60分及以上为及格,60分以下为不及格
# 定义分数为85分
score = 85
if score >= 90:
print("优秀")
elif score >= 80:
print("良好")
elif score >= 60:
print("及格")
else:
print("不及格")
if
语句中嵌套if...else
语句
if 表达式1:
if 表达式2:
语句块1
else:
语句块2
if...else
语句中嵌套if...else
语句
if 表达式1:
if 表达式2:
语句块1
else:
语句块2
else:
if 表达式3:
语句块3
else:
语句块4
需求:判断一个年份是不是闰年
我们先搞清楚什么是闰年
第一种,是普通闰年,能被4整除但不能被100整除(如2008年就是普通闰年,而1900年不是)
第二种,比较少见的,世纪闰年:能被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} 不是闰年!")
很明显这种方式代码可读性非常差
使用如下这种方式更容易理解
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} 不是闰年!")
或者
year = int(input("请输入年份:"))
if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
print(f"{year} 是闰年!")
else:
print(f"{year} 不是闰年!")
# 语法格式
表达式1 if 条件 else 表达式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}")
针对上面的代码,可以使用条件表达式进行简化,代码如下所示:
# 将 a b 中最大的值 赋值给 maximum
a = 2
b = 3
maximum = a if a > b else b
print(f" maximum = {maximum}")
①初始化部分
while ②循环条件:
③循环体
当循环条件为真时,则执行循环体,当循环条件为假时,退出循环。
执行过程:① - ② - ③ - ② - ③ - ② - ③ - … - ②
- 写
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
# 语法格式
for 迭代变量 in 序列:
循环体
其中,迭代变量用于保存读取出的值,序列可以是字符串、列表、元组、集合、字典等等
需求:逐个遍历打印字符串 hello word
中的每个字符
name = "hello word"
for item in name:
# 打印每个字符
print(item)
尽管除字符串以外,其他的序列类型目前没有学习到,但是我们不妨碍我们学习 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)
在 Python
中,for
循环和 while
循环都可以进行嵌套
while
循环中嵌套 while
循环的格式
while 条件表达式1:
while 条件表达式2:
循环体2
循环体1
for 循环中嵌套 for 循环的格式
for 迭代变量1 in 序列1:
for 迭代变量2 in 序列2:
循环体2
循环体1
while 循环中嵌套 for 循环的格式
while 条件表达式:
for 迭代变量 in 序列:
循环体2
循环体1
for 循环中嵌套 while 循环的格式
for 迭代变量 in 序列:
while 条件表达式:
循环体2
循环体1
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
写代码之前,先补讲以下 print()
函数的其他用法
# print() 函数默认拼接符号是 换行符
print("111")
print("222")
# 输出内容为:
"""
111
222
"""
print("111", end="")
print("222", end="")
# 输出内容为:111222
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("") # 换行
Python
中 break
和 continue
他们的作用都是用来结束循环
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个苹果
吃饱了,不吃了
需求:一共吃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个苹果
吃出一个虫子,这个草莓不吃了
吃了4个苹果
吃了5个苹果
在实际开发中,有时候我们会先搭建起程序的整体逻辑结构,但是暂时不去实现某些细节,而是在这些地方加一些注释,方面以后再添加代码,请看下面的例子:
age = int( input("请输入你的年龄:") )
if age < 18 :
print("青少年")
elif 18 <= age < 60:
# 成年人
else:
print("老年人")
当年龄大于等于 18 并且小于 60 时,我们没有使用 print()
语句,而是使用了一个注释,希望以后再处理成年人的情况, 此时就可以通过 pass
语句来实现, 使用 pass
语句比使用注释更加优雅。
修改后的代码如下所示
age = int(input("请输入你的年龄:"))
if age < 18:
print("青少年")
elif 18 <= age < 60:
# 成年人
pass
else:
print("老年人")
match...case
语句又叫模式匹配,Python 3.10
新出的功能,match...case
语法类似于其他面向对象语言中的 switch...case
语句
语法:
match subject:
case <匹配1>:
代码1
case <匹配2>:
代码2
case <匹配3>:
代码3
case _:
代码4
从上到下对
subject
与case
语句中的每个模式进行比较直到确认匹配到一个模式如果没有确认到一个完全的匹配,则如果提供了使用通配符
_
的最后一个case
语句,则它将被用作已匹配模式。 如果没有确认到一个完全的匹配并且不存在使用通配符的case
语句,则整个match
代码块不执行任何操作
subject
可以是数字、字符串、列表、元组、字典、集合、类、枚举模式匹配常用在数字、字符串、枚举这三个中
案列:匹配一个字面值
status = 500
if status == 400:
print("这是400")
elif status == 404:
print("这是404")
elif status == 500:
print("这是500")
else:
print("没有匹配到")
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)
Python语言中基本容器类型的接口如图,基本容器类型的UML类图,以蓝色字体显示的方法名表示抽象方法,必须由具体子类是实现,其他方法有具体实现,子类可以直接继承,
下图是collections.abc
模块中的抽象基类图。
Iterable、Container和Sized
每个容器都应该继承这三个抽象基类,或者实现兼容的协议。
Iterable
通过__iter__()
方法支持迭代
Container
通过__contains__()
方法支持in
运算符
Sized
通过__len__()
方法支持len()
函数
Collection
这个抽象基类是
3.6
新增的,自身没有方法,目的是方便子类化Iterable
、Container
和Sized
Sequence、Mapping和Set
这3个抽象基类是主要的不可变容器类型,而且各自都有可变的子类
Iterator
注意它是
Iterable
的子类
Callable和Hashable
这两个不是容器,只不过因为
Collections.abc
是标准库中定义抽象基类的第一个模块,而它们又太重要了,因此才被放在这里。它们可以在类型检查中用于指定可调用和可哈希的对象
Reversible
Reversible
通过__reversed__()
实现反转,比如序列前后颠倒
序列是最基本的数据结构,是一块用于存放多个值得连续内存空间,并且按一定顺序排列,每个元素都分配一个索引,通过该索引可以取出相应的值。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”) |
列表是由一系列按特定顺序排列的元素组成,所有的元素都放在一个 []
中,两个相邻元素间使用逗号,
分隔。并且同一个列表中,元素的类型可以不同
。
使用list()函数创建列表
list()
函数将序列转换成列表
range()
函数生成数字序列可以使用
list( )
函数直接将range()
函数循环出来的序列结果转换为列
# 创建空列表
list1 = []
# 使用list()函数创建列表
# 创建一个[10~20)之间所有偶数的列表
list2 = list(range(10, 20, 2))
print(list2) # [10, 12, 14, 16, 18]
序列中的每一个元素都有索引(或者叫下标),索引是从0开始递增的,即下标为0表示第一个元素,下标为1表示第2个元素,依次类推.
索引可以是负数,从右向左计数,最后一个元素的索引值是-1,倒数第二个元素的索引值为-2,依次类推,通过索引可以访问序列中的任何元素。
需求:定义一个包含6个元素的列表,要访问它的第3个元素和最后一个元素
# 列表用中括号:[] 表示,定义列表 list, list中有 6 个元素
list = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
# 输出第3个元素
print(list[2])
# 输出最后一个元素
print(list[-1])
切片操作可以访问一定范围内的元素。通过切片操作可以生成一个新的序列。
语法格式:
seqName[start : end : step]
参数说明:
seqName
: 表示序列的名称start
: 表示切片的开始的位置(包括该位置),如若不指定,则默认为0end
: 表示切片结束的位置(不包括该位置),如果不指定,则默认为序列的长度step
: 表示切片的步长,如果省略,则默认为1,当省略步长时,最后一个冒号也省略
通过切片获取列表 [“张三”, “李四”, “王五”, “赵六”, “孙七”, “周八”] 中第3个到第5个元素,再获取第1个、第3个、第5个元素
如果想复制整个序列,可以省略
start
和end
参数都省略,但中间冒号需要保留,例如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])
输出:
[‘王五’, ‘赵六’, ‘孙七’]
[‘张三’, ‘王五’, ‘孙七’]
[‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘孙七’, ‘周八’]
[‘张三’, ‘李四’, ‘王五’][‘张三’, ‘王五’, ‘孙七’]
在Python
中,支持两种相同的序列相加操作。即将两个序列连接,使用 +
号运算符实现。例如将列表相加
在进行序列相加时,相同类型的序列是指,同为列表、元组或集合等,序列中的元素可以不同
不能是列表和元组相加,或者是列表与字符串相加等
# 数字类型的列表
num_list = [57, 11, 22, 48]
# 字符串类型的列表
list = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
print(num_list + list)
# 定义两个字符串序列
str1 = "hello"
str2 = " word"
print(str1 + str2)
输出:
[57, 11, 22, 48, ‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘孙七’, ‘周八’]
使用数字 n
乘以一个序列会生成新的序列。新的序列的内容为原来序列的重复 n
次的结果
需求:将实现一个序列乘以3,生成一个新的序列并输出结果,从而达到 重要的事情说三遍
的效果
list = ["重要的事情说三遍"]
print(list * 3)
str1 = "a"
print(str1 * 6)
输出:
[‘重要的事情说三遍’, ‘重要的事情说三遍’, ‘重要的事情说三遍’]
aaaaaa
使用 in
关键字检查某个元素是否是序列的成员,使用 not in
关键字实现检查某个元素是否不包含在指定序列中。
语法格式:
value in seqName
参数说明:
value
:表示要检查的元素seqName
:表示指定的序列
需求:检查序列 [“张三”, “李四”, “王五”, “赵六”, “孙七”, “周八”] 中
是否包含元素 “李四”
是否包含元素 “周舞”
是否不包含 “孙七”
list = ["张三", "李四", "王五", "赵六", "孙七", "周八"]
print("李四" in list)
print("周舞" in list)
print("孙七" not in list)
输出:
True
False
False
在 Python
中,提供了内置内置函数计算序列的长度、最大值、最小值等
# 数字类型的列表
num_list = [57, 11, 22, 48]
# 计算序列长度
print(len(num_list)) # 4
# 序列中的最小元素
print(min(num_list)) # 11
# 序列中的最大元素
print(max(num_list)) # 57
遍历列表常用的两种方法:
for item in listname
:for
循环遍历列表,只能输出元素的值
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}")
输出:
张三
李四
王五
赵六
孙七
周八
0 - 张三
1 - 李四
2 - 王五
3 - 赵六
4 - 孙七
5 - 周八
增加数据
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', '猪八戒']
输出:
[‘张三’, ‘李四’, ‘王五’]
[‘孙悟空’, ‘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) # []
修改元素
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, ‘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]
输出:
[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]
列表推导式是一种简洁的语法,可以快速创建列表
语法格式:
[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)
# 二维数组 两行三列 2x3
arr = [
[0, 1, 2],
[0, 1, 2]
]
# 获取元素
print(arr[0])
print(arr[0][2])
print(arr[1][1])
需求:使用古诗 “床前明月光,疑是地上霜,举头望明月,低头思故乡” 生成 [[‘床’, ‘前’, ‘明’, ‘月’, ‘光’], [‘疑’, ‘是’, ‘地’, ‘上’, ‘霜’], [‘举’, ‘头’, ‘望’, ‘明’, ‘月’], [‘低’, ‘头’, ‘思’, ‘故’, ‘乡’]] 二维数字,并打印到控制台
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("")
输出:
[[‘床’, ‘前’, ‘明’, ‘月’, ‘光’], [‘疑’, ‘是’, ‘地’, ‘上’, ‘霜’], [‘举’, ‘头’, ‘望’, ‘明’, ‘月’], [‘低’, ‘头’, ‘思’, ‘故’, ‘乡’]]
4
床前明月光
疑是地上霜
举头望明月
低头思故乡
list有如下特点
列表的元素按照顺序有序挨着
索引映射唯一一个数据
列表可以存储重复数据
可以存储任意类型的数据
根据需要动态分配和回收内存
元组(tuple)是另一个重要的序列结构,与列表类似,由一系列按特定顺序排列的元素组成,但是它是不可变序列
, 元素都放在一个()
中,两个相邻元素间使用逗号,
分隔。
元组中只包含一个元素时,需要在元素后面添加逗号
# 创建空元组
tuple1 = ()
print(type(tuple1)) # <class 'tuple'>
# 创建一个元素的元组
# 元组中只包含一个元素时,需要在元素后面添加逗号
tuple2 = ("张三",)
# 创建多个元素
tuple3 = ("张三", "李四", [1, 2, 3], False)
# 小跨号也可以省略不写,只有再循环的时候推荐省略
tuple4 = "张三", "李四", [1, 2, 3], False
print(tuple3)
print(tuple4)
输出:
<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)
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
元组一旦初始化完成,不能增加、修改、删除局部元素,但是可以重新整体赋值
删除数据
对于已创建的元组,不再使用时,可以使用 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)
修改元素
元组是不可的序列,我们不能对单个元素进行修改,但是可以对元组整体重新赋值
tuple6 = (1, 2, 3)
tuple6 = (4, 5, 6)
print(tuple6) # (4, 5, 6)
我们说元组是不可变的,为什么下面这种情况元组的值修改了?
原因:字典中的列表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})
查询和其他常见操作
使用 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
输出:
(10, 12, 14, 16, 18)
12
(12, 14, 16)
2
0
循环遍历元组
方法一:直接使用
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}")
输出:
10
12
14
16
18
for…in enumerate 遍历
0 - 10
1 - 12
2 - 14
3 - 16
4 - 18
元组推导式是一种简洁的语法,可以快速创建元组,它的表现形式和列表推导式类似,只是将推导式中的 []
修改为 ()
语法格式:
(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))
字符串是连续的字符序列,用单引号''
、双引号""
、三引号'''或"""
表示
当字符串包含单引号时,应该使用双引号
当字符串包含双引号时,应该使用单引号
str1 = 'hello word' str2 = "hello word" # 当字符串包含单引号时,应该使用双引号 str3 = "it's a string" # 当字符串包含双引号时,应该使用单引号 str4 = 'hello " word' print(str3) print (str4) # 多行字符串 str5 = ''' 朝辞白帝彩云间, 千里江陵一日还。 两岸猿声啼不住, 轻舟已过万重山。 ''' print(str5) str6 = """ 朝辞白帝彩云间, 千里江陵一日还。 两岸猿声啼不住, 轻舟已过万重山。 """ print(str6)
字符串操作符,下表示例中 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)
字符串查询 index
和 find
,建议使用 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
字符串大小写转换操作
方法名称 | 作用 |
---|---|
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
字符串对齐操作
方法名称 | 作用 |
---|---|
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!
分割字符串操作
方法名称 | 作用 |
---|---|
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())
输出:
[‘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’]
字符串合并与替换操作
方法名称 | 作用 |
---|---|
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
字符串判断操作
方法 | 含义与作用 |
---|---|
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不可打印
字符串的比较操作:
>
, >=
, <
, <=
, ==
, !=
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
方法 | 含义与作用 |
---|---|
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
判断开头结尾字符串操作
方法 | 含义与作用 |
---|---|
startswith(str) | 判断字符串是不是以 str 开头 |
endswith(str) | 判断字符串是不是以 str 结尾 |
str1 = "my name is zhangsan"
print(str1.startswith("my")) # True
print(str1.endswith("is")) # False
字符串的编码与解码操作
方法 | 含义与作用 |
---|---|
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'))
输出:
------------编码---------------
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 解码: 我是中国人
字符串计数操作
方法 | 含义与左右 |
---|---|
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
字符串格式化有好几种方式,Python 3.6
版本开始引入 f-string
格式化字符串,成为了 Python
字符串格式化的首选方法,该方式简单直观、灵活且易于维护。
语法格式:
f"{content:format}"
使用以字母
f
开头的特殊字符串来创建一个字符串模板,用大括号{}括起来的表达式会在运行时被替换成要输出的值。参数说明:
content
:替换并填入字符串的内容,可以是变量、表达式或函数等,
format
:格式描述符,可以省略,后面详细讲述
name = "张三"
str1 = f"我的名字叫:法外狂徒{name}"
print(str1)
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 | 自动在e 和f 或者 E 和F 中切换,既浮点数和科学计数法之前相互切换显示 |
% | 显示百分号,默认显示小数点后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}")
输出:
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}")
输出:
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-31 | 6号:06 |
%H | 24小时制小时数,范围 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)
字典是有序的可变序列
,不支持索引和切片。保存内容是以 键-值对
(也叫 key-value
) 的形式存放,可存储任意类型对象,如字符串、数字、元组等。
创建字典,在 键
和 值
之间使用冒号分割,相邻两个元素使用逗号分隔,所有的元素放在一个大括号里{ }
中。
语法格式:
dictionary = {"key1":"value1","key2":"value2"}
参数说明:
dictionary
:表示字典名称key1
、key2
:表示元素的键,必须是唯一的,并且不可变的,可以是字符串、元组或者数字value1
、value2
:表示元素的值,可以是任何数据,不是必须唯一
通过键读取数据,不能通过索引来获取
字典是无序的、可变的,并且可以任意嵌套
字典的键必须唯一
字典中的键必须不可变
键是不变的,因此可是使用数字、字符串、元组,不能使用列表
# 使用 {} 创建字典
dict1 = {}
dict2 = {"zhansgan": 20, "lisi": 33, "wangwu": 60}
print(dict2)
# 使用 dict() 函数创建字典
dict3 = dict(zhansgan=20, lisi=33, wangwu =60)
print(dict3)
增加/修改数据
语法格式:
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)
输出:
{‘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)
输出:
张三
{‘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)
输出:
{‘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']
遍历字典常用的两种方法:
方法一:
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)
输出:
(‘name’, ‘张三’)
(‘age’, 20)
(‘sex’, ‘男’)
name 张三
age 20
sex 男
name 张三
age 20
sex 男
(0, ‘name’)
(1, ‘age’)
(2, ‘sex’)
0 name
1 age
2 sex
字典推导式是一种简洁的语法,可以快速创建字典
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)
<class ‘zip’>
<zip object at 0x0000018C39E14F80>
[(‘张三’, 18), (‘李四’, 30), (‘王五’, 66)]
{‘张三’: 18, ‘李四’: 30, ‘王五’: 66}
{‘张三’: 18, ‘李四’: 30, ‘王五’: 66}
Python
中,用 set
来表示一个无序不重复
元素的序列,作用就是用来给数据去重
可以使用大括号 { }
或者 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)
输出:
set()
{’ ', ‘d’, ‘r’, ‘w’, ‘e’, ‘l’, ‘o’, ‘h’}
{1, 2, 3}
{‘张三’}
{False, ‘Python’, 20, (‘张三’, ‘张三’)}
{8, 1, 5, 6}
增加数据
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)
输出:
{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
{False, (‘张三’, ‘张三’), ‘李四’, ‘Python’}
{(‘张三’, ‘张三’), ‘李四’, ‘Python’}
(‘张三’, ‘张三’)
李四
{‘Python’}
set()
# 集合中数学的操作 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}
输出:
{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}
列表 | 元组 | 字典 | 集合 | |
---|---|---|---|---|
名称 | list | tuple | dict | set |
定义符号 | [] | () | {key:value} | {} |
是否可变 | 可变 | 不可变 | 可变 | 可变 |
是否可重复 | 可重复 | 可重复 | 可重复 | 不可重复 |
是否有序 | 有序 | 有序 | 3.7+ 有序 | 无序 |
存储方式 | 值 | 值 | 键-值对(键不可以重复) | 键 |
索引 | 支持 | 支持 | 不支持 | 不支持 |
切片 | 支持 | 支持 | 不支持 | 不支持 |
+,* | 支持 | 支持 | 不支持 | 不支持 |
in / not in | 支持 | 支持 | 支持 | 支持 |
# 定义函数:两个数相加,并返回相加后的结果
def add(x, y):
z = x + y
return z
# 调用函数
a = 10
b = 30
print(add(a, b)) # 40
Python
中的对象可分为不可变对象(数字、字符串、元组等)和可变对象(列表、字典、集合等);对应地Python
函数的参数传递有以下两种情况
实参为不可变对象(值传递)
: 当实参为不可变对象时,函数调用是将实参的值复制一份给形参。在函数调用中修改形参时,不会影响函数外面的实参
实参为可变对象(引用传递)
:当实参为可变对象时,函数调用是将实参的引用复制给形参。在函数调用中修改形参时,函数外面的实参值也会随之变化
# 定义函数:向列表中添加数据
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]
Python
中函数传递参数的形式主要有以下4种,分别为位置传递、关键字传递、默认值传递、可变参数
位置参数
:也称必备参数,必须按照正确的顺序传递到函数中,既调用时的数量和位置必须和定义的一样def fun1(a,b,c)
return a+b+c
# 位置参数传递
print(fun1(1,2,3))
# 位置参数调用时候,参数数量必须和定义的一致
# print(fun1(1,2))
关键字参数
:根据每个参数的名字传递参数,不用遵守位置的对应关系def fun1(a, b, c):
return a + b + c
# 关键字参数传递,不用遵守位置的对应关系
print(fun1(1, c=3, b=2))
参数默认值
:在定义函数时,直接指定形参的默认值;当没有传入参数时,则直接使用定义函数时设置的默认值再定义函数时,指定默认值的参数必须在最后(如果有可变参数则放在可变参数的前面),否则产生语法错误
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
可变参数
: 可变参数主要有两种形式,一种是 *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.如果一个函数中,同时有 *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)
匿名函数就是没有名字的函数,在 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
# 原始方法 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))
语法格式:
filter(function_or_None, iterable)
参数说明:
function_or_None
:如果是函数,作用是对iterable
中的每个元素判断是否符合特定条件,然后返回True
或False
,最后将返回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]
由于引入列表推导式和生成器表达式,因此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]
局部变量
:在函数内部定义的变量。这个变量只能在定义这个变量的函数内部使用
全局变量
:在函数外部定义的变量。所有函数内部和外部都可以使用这个变量(有两种方式,第一种定义在函数外;第二种定义在函数内,用 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)
如今主流的软件开发思想有两种:一个是面向过程,另一个是面向对象。面向过程出现得较早,典型代表为C语言,开发中小型项目的效率很高,但是很难适用于如今主流的大中型项目开发场景。面向对象则出现得更晚一些,典型代表为Java
或C++
等语言,更加适合用于大型开发场景。两种开发思想各有长短。
对于面向过程的思想:
需要实现一个功能的时候,看重的是开发的步骤和过程,每一个步骤都需要自己亲力亲为,需要自己编写代码(自己来做)
对于面向对象的思想:
当需要实现一个功能的时候,看重的并不是过程和步骤,而是关心谁帮我做这件事(偷懒,找人帮我做)
生活举例:
洗衣服
面向过程(手洗)
:脱衣服、找一个盆、加水、加洗衣粉、浸泡30分钟、搓洗、拧衣服、倒掉水、再加水、漂洗、拧衣服、倒掉水、晾衣服。
面向对象(机洗)
:脱衣服、放入洗衣机、按下开关、拿出衣服晾晒。买电脑
面向过程(自己买)
:需要电脑、查询参数信息、横向比较机型、了解打折信息、与店家讨价还价、下单、收快递、开机验货、确认收货。
面向对象(找人买)
:需要电脑、找秘书帮我买、收电脑。
面向对象的三大特征有:封装、继承、多态
类:
抽象的,比如一张 “手机设计图”
对象:
具体的,比如一部 “真正的手机,比如华为mate50”
类由有属性、行为两个组成部分
属性:
事物的特征描述信息,用于描述某个特征 “是什么”
行为:
事物 “能做什么”
案例:
人类设计:
事物名称(也叫类名): 人(Person)
属性: 姓名、年龄(age)、性别(sex)等
行为(也叫方法): 跑(run)
狗类的设计:
类名: 狗(Dog)
属性: 品种、毛色、性别、名字
方法: 叫、跑、咬人、吃、摇尾巴等
类的组成:
属性
(又叫成员变量):分为共享属性、实列属性两种
方法
(又叫成员方法):实列方法、静态方法、类方法
# 人类 # 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()
访问限制
名称 | 列子 | 含义 |
---|---|---|
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)
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())
@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) # 女
封装
就是我写了一个类,我将一个类的属性、方法全部包装到了一个类中。我对类中某些方法和属性进行了隐藏,因为我不想让外部了解我的实现机理或属性,但是会留出一些公开的方法来供外部间接调用这些封装
好的属性和方法,这就是封装!
继承
object
是所有类的父类,如果一个类没有继承任何类,则默认继承object
双下划线开头的成员属性(__xx),最终实列话对象后,成员属性变成 “_类名__xx”
公共成员变量、受保护的成员变量,谁实列化,属性就挂在该实列化对象身上
有单下划线的开头成员属性,子类可以直接访问;有双下划线开头的成员属性,子类不能访问;公共的成员变量和共享变量,子类可以任意访问
有继承关系的对象实列化,调用方法和属性的时候,依次查询获取:实例化对象-> 实例化对象的类 ->父类
如果子类对继承自父类的某个方法不满意,可以在子类中对其进行重新编写;子类重写后的方法中可以通过
super().xxx()
调用父类中被重写的方法
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()
接口、协议、抽象基类、虚拟子类
Python
没有多态,因为 Python
自诞生以来默认使用的鸭子类型设计理念
鸭子类型:忽略对象的类型,转而关注对象有没有实现所需要的方法、签名、语义, Python
自诞生以来默认使用的鸭子类型, 鸭子协议是Python
核心思想
抽象基类:指的是collections.abc
和自定义的抽象基类
在
Python
的abc
模块中,ABC
和ABCMeta
是两个关键类,用于创建抽象基类(Abstract Base Class
)
ABC
是一个可继承的基类,用于定义抽象基类。当一个类继承自
ABC
时,可以通过使用@abstractmethod
装饰器来定义抽象方法。抽象方法是一种声明,用于指示子类必须实现这些方法。
子类必须实现抽象基类中定义的所有抽象方法,否则在实例化时会引发
TypeError
异常
ABC
类本身并不强制要求实现任何方法或属性,它主要用于定义抽象方法和作为抽象基类的标识
ABCMeta
是一个元类(metaclass
),用于定义抽象基类的元信息。通过将
ABCMeta
作为元类,可以在类定义阶段对类进行检查和修饰。
ABCMeta
通过在类定义中使用metaclass
参数或在类的基类列表中包含ABCMeta
来指定
ABCMeta
元类提供了一些功能,例如检查子类是否实现了抽象方法、注册具体实现类等总结:
ABC
是一个可继承的基类,用于定义抽象基类,并通过装饰器@abstractmethod
定义抽象方法
ABCMeta
是一个元类,用于定义抽象基类的元信息,并提供了一些功能来检查和修饰类定义
大鹅类型:在鸭子类型的基础上补充的大饿类型, 自 Python 2.6
开始,由抽象基类支持的方式,该方式会在运行时检查对象是否符合抽象的基本要求,大鹅类型的一个基本特征是,即便不继承,也有办法把一个类注册为抽象类的虚拟子类。虚拟子类实现抽象基类的的全部抽象方法,同样达到继承的效果,也符合鸭子类型。
大鹅类型要求:
- 定义抽象基类的子类,明确表明你在实现抽象基类的接口
虚拟子类:不通过继承,把一个类注册为抽象基类的虚拟子类。注册虚拟子类的方式是在抽象基类上调用 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)
自定义抽象基类:
# 从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 鸭子类型的初衷
总结:
通过继承来实现抽象基类的抽象方法固然可以,但是违背了
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()
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
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'>
比较两个对象的值是否相同
,is
比较两个对象的内存地址==
运算符比较两个对象时,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未知 # 习惯常量均为大写,多个单词使用下划线连接 # 方式一:虽然是常量,但是是可变的 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未知 # 习惯常量均为大写,多个单词使用下划线连接 # 枚举 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
如下是常见的类型注解
类型 | 描述 |
---|---|
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)
创建模块可以将模块中相关的代码(变量定义和函数定义等)编写在一个单独的文件中,并且将该文件命名为模块名+.py
的形式,也就是说,创建模块,实际就是创建一个.py
文件
创建模块时,设置的模块名尽量不要与
Python
自带的标准模块名称相同模块文件的扩展名必须是
.py
import modulename [as alias]
参数说明:
modulename:要导入模块的名称
[as alias]:为模块起的别名
from modelname import member
参数说明:
modelname
:模块名称,区分字母大小写,需要和定义模块时设置的模块名称的大小写保持一致member
:用于指定要导入的变量、函数或者类等。可以同时导入多个定义,各个定义之间用逗号 “,” 隔开。如果想导入全部定义,也可以使用通配符星号 “**” 代替
使用模块可以避免函数名和变量名重名引发冲突。那么,如果模块名重复应该怎么办呢?在Python中,提出了包(Packaage)的概念。包括每一个分层次的目录结构,它将一组功能相近的模块组织在一个目录下。这样,既可以起到规范代码的作用,又能避免模块名重名引起冲突
包与目录的区别:
包含 __init__.py
文件的目录称为包
目录里通常不包含 __init__.py
文件
包的导入
import 包名.模块名
错误
异常
在 Python
中,提供了try
语句捕获并处理异常
语句 | 作用 |
---|---|
try | 在try 后except 前的语句将会由except 来捕获 |
except | 语句后跟一个异常类(继承自BaseException 或其子类),该except 会捕捉该类或其子孙类实例。如果未捕获到且后续还有except 语句则会由后续语句继续捕获,如果到最后一个except 语句仍未被捕获,则抛出到调用者 |
else | 如果没有发生异常,执行else 中的语句 |
finally | 在finally 语句中的代码,不管是否发生异常都会被执行 |
常见的语句如下:
try...except
try...except...else
try...except...except...else
try...except...else...finally
# 语句一 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 里面的代码了!")
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)
自定义异常
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)
在Python
中,想要操作文件需要先创建或者打开指定的文件并创建文件对象。这可以通过内置的open()
函数实现。open()
函数的基本语法格式如下:
file = open(filename[,mode[,buffering]])
参数说明:
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!
方法 | 含义 |
---|---|
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())
春眠不觉晓
处处闻啼鸟
夜来风雨声
花落知多少
春眠不觉晓[‘春眠不觉晓\n’, ‘处处闻啼鸟\n’, ‘夜来风雨声\n’, ‘花落知多少’]
在 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) | 从一个目录中提取文件的父路径 |
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 来下载包)
国内常用的 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
修改本地的 pip 配置文件
pip 有 3 个“级别”的配置文件:
global
:系统范围的配置文件,跨用户共享user
:每个用户的配置文件site
:每个环境的配置文件;即每个虚拟环境加载顺序: global -> user -> site
Windows(学习、开发环境)
global
: C:\ProgramData\pip\pip.inuser
: %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.confuser
: 如果$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.confsite
:指定虚拟环境(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
index-url = https://pypi.tuna.tsinghua.edu.cn/simple 含义: 设置加速源为清华源
timeout = 10 含义: http 连接超时设置10秒
trusted-host = pypi.tuna.tsinghua.edu.cn 含义: 信任的镜像源的域名或主机,否则会有告警
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
virtualenv 使用 python 自带的 pip 安装依赖
常用命令总结
意义 | pip | conda |
---|---|---|
查看版本 | pip --version | conda --version |
查看已安装的依赖清单 | pip list | conda list |
在当前环境中安装指定版本的包 | pip install 包名==版本号 | conda install 包名==版本号 |
删除当前环境中的包 | pip uninstall 包名 | conda remove 包名 |
创建环境 | conda create -n 环境名 python=版本号 | |
激活环境 | activate 环境名 |
方法一 :requirements.txt
方法二 : environment.yml
requirements.txt 与 environment.yml
它们的目的都是为了帮助别人在别处恢复出相同的环境,所以是先构建好了项目环境后,再生成,用来记录、恢复环境所需的依赖环境
这个方法不推荐,因为只会导出你使用 pip 安装的依赖包,不会导出 conda 虚拟环境安装的依赖包,并不适用于虚拟环境的迁移的应用场景。
pip install numpy
conda install Django
# 生成 requirements.txt
pip freeze > requirements.txt
# 从 requirements.txt 安装依赖
pip install -r requirements.txt
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 新虚拟环境名
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
上面所示的 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
JSON(JavaScript Object Notation) 是一种轻量级的、基于文本的、开放的数据交换格式,易于人阅读和编写。JSON 在 Web 开发领域有着举足轻重的地位,必须熟悉 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
}
null(空)
python 原始类型与 json 类型相互转化对照表:
json | python | 解释 |
---|---|---|
number | int, float | 0、3.14、17 |
string | str | “张三” |
true | True | 布尔 True |
false | False | 布尔 False |
array | list,tuple | [1,2,3]、[“打游戏”, “打篮球”, “爬山”] |
object | dict | { “username”: “张三”, “age”: 17, “like”: [“打游戏”, “打篮球”, “爬山”], “is_adult”: false, “phone”: null } |
null | None | 空 |
在使用 json 时,有以下几点需要注意:
json 是一段包裹在花括号
{}
中的数据,数据由若干key-value 键值对组成key-value 键值对中的 key 必须是字符串,value 可以是 json 中的任意类型(数字、字符串、布尔值、数组、对象、null;)
键必须是唯一的,不能重复,否则后定义的键-值对会覆盖前面定义的键-值对
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)
们在 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()))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。