
学习python,这篇就够
全面讲解python基础知识,应有尽有。
此文章全面介绍了python语言的基础知识,不过还有第7章文件与数据格式化还未介绍,短期内会及时补充上,不用担心。
目录
1. Python概述
1.1 认识python
1.1.1 python的发展历程
1.1.2 python语言的特点
python的优点
- 简洁
- 语法优美
- 简单易学
- 开源
- 可移植性好:python是一种解释型语言,可以在任何安装有python解释器的平台执行,python程序可以不加修饰地在任何平台上运行。
- 扩展性号
- 类库丰富
- 通用灵活
- 模式多样
- 良好的中文支持
python的缺点 - 执行效率不够高,python程序没有C++、Java编写的程序高校
- python3.x和python2.x的兼容性不够好
1.2 Python解释器的安装和python程序的运行
python是一种面向对象的解释型程序设计语言,python程序的执行需要借助python解释器完成,计算机中安装python解释器并配置好python开发环境后,开发人员可通过不同方式编写和运行程序。
1.2.1 安装python解释器
在python官网可以下载python解释器,python解释器针对不同平台分为多个版本。下面演示如何在Windows 64位操作系统中安装python解释器。(此部分请参考网上,这里不过多解释)
1.2.2 python程序的运行方式
python程序的运行方式有两种:
-
交互式:指python解释器逐行接收python代码并即使响应
-
文件式:也称批量式,指先将python代码保存在文件中,再启动python解释器批量解释代码
1.交互式
通过python解释器或控制台都能以相同的操作以交互式运行python程序,以控制台为例,进入python环境后,在命令行>>>后输入代码:
>>> print("hello world")
按回车,控制台立刻打印运行结果:
2.文件式
创建python文件(后缀为.py的文件),在里面编写python程序并保存,然后在该目录下打开命令行窗口,在命令提示符>是=后输入命令:python 文件名.py,即可运行python程序
1.3 python的开发工具
本节对常用的python开发工具进行介绍,本文章使用的开发工具为PyCharm。
1.3.1 python常用的开发工具
查阅资料了解即可
1.3.2 PyCharm的下载与安装
查阅资料
1.3.3 使用PyCharm编写python程序
打开PyCharm,点击新建一个项目:
进入如下窗口:
- Location文本框用于设置项目的路径名
- New environment using选项:用于为项目创建虚拟环境
- previously configured interpreter选项:用于配置使用已存在的环境
在D:\pythonProject路径下创建项目python_study,选择previously configured interpreter选项并配置python解释器,如下图:
之后点击create按钮完成项目创建并进入项目管理界面:
在项目下创建python文件:
给新建的python文件命名为first,并回车:
python文件创建完成后,如图:
接下来就可以在python文件中编写代码了:
编写完代码后,右键要执行的python文件,选择Run执行该文件
执行结果如下:
1.4 python模块
为了保证代码的可维护性,开发人员通常将一些功能性代码放在其他文件中,这种用于存放功能性代码的文件就是模块,文件名就是模块名。
作为一种强大且便携的编程语言,python支持以模块的形式组织代码。python内置了一些标准模块,标准模块可以直接导入并使用;python的使用者也贡献了丰富且强大的第三方模块,第三方模块则需要先安装。
本节先介绍如何安装模块,再解释如何导入和使用模块。
1.4.1 模块的安装
利用python内置的pip工具(安装python3.8时会自动安装该工具)可以安装python的第三方模块,该工具可在命令行中使用。
语法格式:
pip install 模块名
注意:pip是在线工具,它需要联网才能获取模块资源。
例如安装用于开发游戏的pygame模块:
pip install pygame
1.4.2 模块的导入与使用
导入:在使用模块中定义的内容前,需要先导入模块到当前程序中,python使用import关键字导入指定的模块,语法如下:
import 模块1,模块2,...
例如在程序中导入pygame模块:
import pygame
使用:导入模块后,通过“模块名.”的方式调用模块中的内容,语法格式如下:
模块名.函数
模块名.变量
上面的模块导入方式可以避免多个模块中存在同名函数时使代码产生歧义,但不存在同名函数时,可以使用:from 模块名 import ...的方式导入模块中特定的内容,然后就可以直接在程序中使用这个特定的内容,例如:将pygame模块中的init()函数导入程序,并直接使用该函数:
from pygame import init
init()
2. python基础
2.1 代码格式
2.1.1 注释
作用:用于标识代码的含义和功能,程序运行时python解释器会忽略注释。
单行注释:用于说明当前行或之后代码的功能
print("hello world!") # 单行注释,以“#”开头
多行注释:主要用于说明函数或类的功能,因此多行注释也称为说明文档。
"""
多行注释是:3对双引号或单引号包裹的语句
"""
print("hello world!")
2.1.2 缩进
- Python代码使用缩进确定代码之间的逻辑关系和层次关系。
- 缩进可以通过tab键或空格键控制
- 空格是python3首选的缩进方法,一般使用4个空格表示一级缩进,python3不允许混合使用tab键和空格键。
例如:
if True:
print("True")
else:
print("False")
python要求同一代码块中的每行代码必须有相同的缩进量,代码缩进量的不同会导致代码语义的改变,程序中不能出现无意义或不规范的缩进,否则运行时会出现错误。
错误案例:
2.1.3 语句行换
python官方建议每行代码不超过79个字符,如果代码过长应换行。python会将圆括号、中括号、大括号中的行进行隐式连接,例如:
print("python官方建议每行代码不超过79个字符,如果代码过长应换行,python会将圆"
"括号、中括号、大括号中的行进行隐式连接pytho"
"n官方建议每行代码不超过79个字符,如果代码过长应换行,python会将圆括号、中括号、大"
"括号中的行进行隐式连接python官方建议每行代码不超过79个字符,如"
"果代码过长应换行,python会将圆括号、中括号、大括号中的行进行隐式连接")
2.2 标识符和关键字
2.2.1 标识符
作用:标识变量、函数、类、模块或其他对象的名称
python中标识符的规则:
- 由字母、数字、下划线组成,且不能以数字开头
- 区分大小写
- 不能使用python中的关键字作为标识符
python中对于标识符命名的建议:
- 标识符最好使用有意义的名称,以便代码可读性更好,例如使用name表示姓名
- 常量名使用大写的单个单词或使用下划线分隔的多个单词
- 模块名、函数名使用小写的单个单词 或 由下划线分隔的多个小写单词
- 类名每个单词的首字母大写
2.2.2 关键字
关键字是python中已使用的、不允许开发人员重复定义的标识符,python3中一共定义了35个关键字,这些关键字存储在keyword模块中的kwlist变量里,通过访问kwlist变量可查看python中的关键字,例如:
import keyword
print(keyword.kwlist)
2.3 变量和数据类型
2.3.1 变量
变量就是用来存储数据的容器,变量是存储在内存中的值,这就意味着在创建变量时会在内存中开辟一个空间,python使用变量名来标识不同的内存单元。
定义变量的语法格式:
变量名 = 值
- python中定义变量不需要声明变量的数据类型
- 向变量赋值时,python会根据赋给变量的值,自动声明变量的类型,自动创建变量
- 每个变量在使用前都必须赋值,变量赋值后该变量才会被创建。
例如:
#定义变量data
data = 100
#定义变量后通过变量名访问变量里面的数据
print(data)
2.3.2 数据类型
根据数据存储形式的不同,python中的数据类型有:
- 数字类型:数字类型又分为整型(int)、浮点型(float)、复数类型(complex)和布尔类型(bool),分别对应数学中的整数、小数、复数,bool是int型的子类,只有True和False两种取值,数字类型的实例:
-
整型: 0 101 -239 浮点型: 3.1415 4.2E-10 -2.334E-9 复数类型:3.12+1.2.3j 布尔类型:True False
-
- 字符串:字符串是一个由单引号、双引号、三引号括起来的有序字符集合。例如:
-
'使用单引号包裹的字符串' "使用双引号包裹的字符串" '''使用三引号包裹的字符串'''
-
- 列表:是多个元素的集合,它可以存储任意数量、任意类型的元素,且可以被修改。Python中使用"[]"方括号表示(创建)列表,列表中的元素以逗号分隔,例如:
-
[1,2,'hello']
-
- 元组:元组与列表的作用相似,它可以保存任意数量、任意类型的元素,但不能被修改。python中使用"()"小括号表示(创建)元组,元组中的元素以逗号分隔,例如:
-
(1,2,'hello')
-
- 集合:与列表、元组类似,可以存储任意数量、任意类型的元素,但集合中的元素无序且唯一。使用"{}"表示(创建)集合,集合中的元素以逗号分隔,例如:
-
{'apple','orange',1}
-
- 字典:字典中的每个元素都是 key:value形式的键值对,键不能重复。python中使用"{}"创建字典,字典中的元素用逗号分隔,例如:
-
{"name":"zhangsan","age":18}
-
查看变量的数据类型
语法格式:type(变量名)
dict_demo = {"name":"张三","age":17}
print(type(dict_demo))
输出结果:<class 'dict'>
2.3.3 变量的输入和输出
input()函数:用于接收用户键盘输入的数据,返回一个字符串类型的数据,其语法格式:
input([prompt])
参数:
- prompt:可选,用于设置输入时的提示信息
print()函数:用于向控制台输出数据,其语法格式为:
print(*objects,sep=' ',end='\n',file=sys.stdout)
参数:
- objects:表示输出的对象,输出多个对象时需要用逗号分隔各个对象
- sep:设定分隔符,默认使用空格作为分隔
- end:设定输出以什么结尾,默认值为换行符
- file:表示数据输出的文件对象
2.5 数字类型
2.5.1 整数类型
整数类型(int)简称为整型,用于表示整数,python3中整型数据的长度没有限制,只要计算机内存够大,无须考虑内存溢出问题。
整型数据常用的计数方式有4种,分别是二进制(以0b或0B开头),八进制(以0o或0O开头),十进制和十六进制(以0x或0X开头),下面用4种计数方式表示整型数据5
5 #十进制
0b101 #二进制
0o5 #八进制
0x5 #十六进制
python内置了用于转换数据进制的函数:bin()、oct()、int()、hex()
函数 | 说明 |
bin(x) | 将x转换为二进制数据 |
oct(x) | 将x转换为八进制数据 |
int(x) | 将x转换为十进制数据 |
hex(x) | 将x转换为十六进制数据 |
示例代码:
data = 10
bin_data = 0b1010
print(bin(data))
print(oct(data))
print(int(bin_data))
print(hex(data))
运行结果:
0b1010
0o12
10
0xa
2.5.2 浮点型
用于表示小数,python中的浮点型数据一般以十进制表示,例如:1.0,1.22。
2.5.3 复数类型
复数由实部和虚部组成,它的一般形式为real+imagj,real是实部,imag是虚部,j是虚部单位,示例代码:
#定义两个复数类型的变量
complex_demo = 1 + 2j
complex_demo1 = 2j
#通过real和imag属性可以获取复数的实部和虚部
print(complex_demo.real)
print(complex_demo.imag)
2.5.4 布尔类型
布尔类型是特殊的整型,其True对应整数1,False对应整数0。
python中常见的布尔值为False的数据如下:
- None
- False
- 任何数字类型的0,如0、0.0、0j
- 任何空序列,如""、()、[]
- 空字典,如{}
python中可以使用bool()函数查看数据的布尔值,例如:
print(bool(0))
print(bool(""))
print(bool(1))
2.5.5 数字类型的转换
python内置了一系列用于强制转换数据类型的函数:
函数名 | 说明 |
int(x[,base]) | 将x转换为整型数据 |
float(x) | 将x转换为浮点型数据 |
complex(x) | 将x转换为复数类型 |
num_one = 2
num_two = 2.2
print(int(num_two)) #将浮点型转换为整型,只会保留整数部分
print(float(num_one)) #将整型转换为浮点型
print(complex(num_one)) #将整型转换为复数类型
2.6 运算符
参考:点这里
3. 流程控制
3.1 条件语句
3.1.1 if语句
该语句由关键字if、判断条件、冒号组成,语法如下:
if 判断条件:
代码块
执行流程:if语句的判断条件为True,则执行代码块,否则如果为False,则不执行代码块,程序继续向下执行。
注意:
- 1、每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
- 2、使用缩进来划分语句块,相同缩进数的语句在一起组成一个语句块。
3.2.1 if-else语句
if-else语句的语法格式如下:
if 判断条件:
代码块1
else:
代码块2
执行流程:如果判断条件成立,执行if语句后面的代码块1,否则执行else语句后面的代码块2。
注意:
- 1、每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
- 2、使用缩进来划分语句块,相同缩进数的语句在一起组成一个语句块。
3.1.3 if-elif-else语句
if-elif-else语句的语法格式:
if 判断条件1:
代码块1
elif 判断条件2:
代码块2
elif 判断条件3:
代码块3
...
else:
代码块n
执行流程:如果if语句的判断条件成立,执行if语句之后的代码块1,如果if条件不成立,判断elif语句的判断条件2,成立则执行elif语句后面的代码块2,否则继续向下执行,以此类推,直到所有的判断条件均不成立,执行else语句后面的代码块。
注意:
- 1、每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
- 2、使用缩进来划分语句块,相同缩进数的语句在一起组成一个语句块。
3.1.4 if嵌套
python中和C、Java一样,支持if语句的嵌套。
3.3 循环语句
3.3.1 while语句
while语句一般用于实现条件循环,该语句由while关键字、循环条件、冒号组成。
while语句的语法格式如下:
while 循环条件:
循环体
执行流程:当执行while语句时,如果循环条件为True,则执行循环体中的语句,执行完成循环体中的语句后,再次判断循环条件是否为True,如此反复,直到循环条件为False时终止循环,执行循环后的代码。
3.3.2 for...in语句
for语句一般用于循环遍历,依次访问目标对象中的每一个元素,例如逐个访问字符串中的字符。
for语句的语法格式如下:
for 临时变量 in 目标对象:
循环体
例如:使用for...in循环遍历字符串中的每个字符。
for char in "python":
print(char)
运行结果:
for...in语句和range()函数搭配使用,range()函数可以生成一个由整数组成的可迭代对象,例如:
range([start,]stop[,step])函数:
- start:从start开始计数,默认为0
- stop:到stop结束,不包括stop
- step:设置步长,默认为1
例如:
for x in range(1,6):
print(x)
运行结果:
3.3.3 循环嵌套
python和C、Java一样,同样支持循环的嵌套
3.5 跳转语句
3.5.1 break语句
作用:结束整个循环,如果循环中使用了break语句,程序执行到break语句时就会结束整个循环;如果循环嵌套使用了break语句,程序执行到break语句时会结束本层循环。
break语句通常和if语句配合使用,以便在满足条件时结束循环。
例如:使用for循环遍历字符串"python"时,遍历到o时就结束循环。
for x in "python":
if(x == "o"):
break
print(x,end=" ")
运行结果:
3.5.2 continue语句
作用:在满足条件时跳出本次循环,强制执行下一次循环,通常也和if语句配合使用。
例如:在使用for循环遍历字符串"python"时,遍历到字符"o"时使用continue语句跳出本次循环。
for x in "python":
if(x == "o"):
continue
print(x,end=" ")
运行结果:
4. 字符串
4.1 字符串介绍
字符串:是一个由单引号、双引号、三引号括起来的任意内容,其中单引号和双引号通常用于定义单行字符串,三引号通常用于定义多行字符串。
- 字符串一旦创建就不可修改,对字符串修改,就会产生新的字符串。
注意:
- 如果字符串中含有双引号,则需要用其他两种引号定义字符串
- 如果字符串中含有单引号,则需要用其他两种引号定义字符串
- 如果字符串中含有三引号,则需要用其他两种引号定义字符串
- 还可以在字符串中的引号前使用“\”反斜杠,表示将"\"反斜杠后面的引号作为一个普通字符,而非特殊字符
转义字符:一些普通字符与反斜杠组合后会产生新的含义,转义字符通常用于表示一些无法显示的字符,例如空格、回车等。
python中常用的转义字符
\b | 退格(Backspace) |
\n | 换行 |
\v | 纵向制表符 |
\t | 横向制表符 |
\r | 回车 |
原始字符串:在字符串开始的引号前添加r或R,使其成为原始字符串,原始字符串可以忽略字符串中的转义字符,例如以下代码:
print(r'\n,\r,\b')
运行结果:
去掉r后:
print('\n,\r,\b')
运行结果:
4.2 格式化字符串
格式化字符串:将指定的字符串转换为想要的格式。
python中有3种格式化字符串的方法:
- 使用%格式化
- 使用format()方法格式化
- 使用f-string格式化
这三种方法的占位符和真实数据是按顺序一一对应来替换的
4.2.1 使用%格式化字符串
语法格式:
字符串 % 真实数据
说明:
- 字符串:在该字符串中编写 将来要用真实数据替换的格式符
- 真实数据:要替换格式符的真实数据
- %代表执行格式化操作,将字符串中的格式符 替换 为真实数据
下图是pyhton中常见的格式符:
注意:真实数据的数据类型必须和格式符代表的数据类型一致,不然会出现异常。
案例1:
value = 10
format = '我今年%d岁'
print(format % value)
运行结果:
案例2:使用多个格式符进行格式化操作时,替换的数据要以元组的形式存储。
name = '张浅'
age = 27
address = '地球中国'
print("---------------------------")
print("姓名:%s" % name)
print("年龄:%d岁\n家庭住址:%s" % (age,address))
print("---------------------------")
运行结果:
4.2.2 使用format方法格式化字符串
为了更直观、便捷地格式化字符串,可以使用format()方法来格式化字符串,语法如下:
需要格式化的字符串.format(要替换{}的真实数据1,要替换{}的真实数据2,...)
说明:
- 需要格式化的字符串:里面用{}来给真实数据占位
案例1:
name = '张浅'
string = '姓名:{}'
print(string.format(name))
运行结果:
案例2:
name = '张浅'
age = 23
string = '姓名:{}\n年龄:{}'
print(string.format(name,age))
运行结果:
字符串中的{}还可以指定编号,格式化字符串时解释器会按编号取format()方法中的真实数据,这些真实数据的索引从0开始编号,例如:
name = '张浅'
age = 23
string = '姓名:{1}\n年龄:{0}'
print(string.format(age,name))
运行结果:
字符串中的{}还可以指定名称,字符串在被格式化时python解释器会按给真实数据绑定的名称来替换{}中的名称,例如:
name = '张浅'
age = 23
weight = 65
string = '姓名:{name}\n年龄:{age}\n体重:{weight}kg'
print(string.format(name=name,weight=weight,age=age))
运行结果:
字符串中的{}可以指定替换的浮点型数据的精度,浮点型数据在被格式化时会按指定的精度进行替换,例如:
points = 19
total = 22
print("所占百分比:{:.2%}".format(points/total)) #保留两位小数
运行结果:
4.2.3 使用f-string格式化字符串
f-string是一种更为简洁的格式化字符串的方式。
语法:在字符串前面添加f或F,然后在字符串中用“{变量名}”的方式来将变量安到这个位置上
案例:
age = 20
gender = '男'
print(f"年龄:{age},性别:{gender}")
运行结果:
4.3 字符串中的常见操作
字符串的操作在实际应用中非常常见,而python内置了很多操作字符串方法。
以下讲的字符串操作方法中,除了字符串连接方法join(),其余都是用要操作的字符串调用方法。
4.3.1 字符串的查找
python中使用find()方法来实现字符串查找功能:
str.find(sub[,start[,end]])
功能:在字符串中查找是否包含指定的子串,如果包含则返回子串首次出现的索引,否则返回-1.
参数:
- sub:指定的子串
- start:开始索引,默认为0,指定从父串的哪个位置开始查找
- end:结束索引,默认为字符串的长度,指定查找到父串的哪个位置
- start和end省略时,查找整个字符串
例如,查找't'是否在字符串'python'中。
str = 'python'
word = 'th'
result = str.find(word)
print(result)
运行结果:
4.3.2 字符串替换
使用字符串对象的replace()方法实现字符串替换:
str.replace(old,new[,count])
功能:将当前字符串中 指定的子串 替换为 新的子串 ,并返回替换后的新字符串。
参数:
- old:要替换的子串
- new:新的子串
- count:替换旧子串的次数,默认全部替换
案例:
str = "All things Are difficult before they Are easy."
new_str = str.replace('Are','are')
print(new_str)
运行结果:
4.3.3 字符串分割
使用split()方法实现字符串分割的功能
str.split(sep=None,maxsplit=-1)
功能:按照指定的分隔符,对当前字符串进行分割,会返回一个分割后的子串组成的列表。
参数:
- sep:指定分隔符,默认为空字符
- maxsplit:分割次数,默认值为-1,表示无限次分割
案例:
str = "All things Are difficult before they Are easy."
print(str.split())
运行:
4.3.4 删除字符串中的指定字符
字符串头部或尾部中可能会包含一些无用的字符(如空格),在处理字符串前往往需要先删除无用的字符,python中的strip()、lstrip()、rstrip()方法可以删除字符串头部或尾部的指定字符。
注意:这些操作不会修改原始字符串,而是返回一个删除指定字符后的新字符串。
这里直接上案例:
old_string = " Life is short, Use Python ! "
#移除字符串头部和尾部的指定字符,默认移除空格字符
print(old_string.strip())
#移除字符串头部的指定字符,默认移除空格字符
print(old_string.lstrip())
#移除字符串尾部的指定字符,默认移除空格字符
print(old_string.rstrip())
运行结果:
4.3.5 字符串大小写转换
old_string = "hello woRld"
#将字符串中的字母全部转换为大写字母
print(old_string.upper())
#将字符串中的字母全部转换为小写字母
print(old_string.lower())
#将字符串中的第一个字母转换为大写字母,其余字符转换为小写
print(old_string.capitalize())
#将字符串中每个单词的首字母转换为大写字母,其余字符转换为小写
print(old_string.title())
运行:
4.3.6 字符串对齐
center(width, fillchar=' '):
- 该方法将字符串居中对齐,并在左右两侧填充空格或指定字符 fillchar,使得最终字符串长度为 width。
- 如果未提供 fillchar,默认使用空格填充。
- 返回填充后的新字符串。
ljust(width, fillchar=' '):
- 该方法将字符串左对齐,并在右侧填充空格或指定字符 fillchar,使得最终字符串长度为 width。
- 如果未提供 fillchar,默认使用空格填充。
- 返回填充后的新字符串。
rjust(width, fillchar=' '):
- 该方法将字符串右对齐,并在左侧填充空格或指定字符 fillchar,使得最终字符串长度为 width。
- 如果未提供 fillchar,默认使用空格填充。
- 返回填充后的新字符串。
# 使用center()方法
text = "Python"
centered_text = text.center(10, '*')
print(centered_text) # 输出:**Python**
# 使用ljust()方法
left_aligned_text = text.ljust(10, '-')
print(left_aligned_text) # 输出:Python----
# 使用rjust()方法
right_aligned_text = text.rjust(10, '+')
print(right_aligned_text) # 输出:++++Python
运行:
4.3.7 字符串连接
str.join(iterable)
功能:用指定的字符str,连接字符串中的各个字符,并返回新的字符串
参数:
- 要连接的字符串
symbol = '*'
world = 'python'
print(symbol.join(world))
运行:
4.3.8 连接字符串
python中还可以使用"+"运算符连接字符串,例如:
start = 'py'
end = 'thon'
print(start + end)
运行:
5. 组合数据类型
5.1 认识组合数据类型
组合数据类型可以将多个相同类型或不同类型的数据组合成一个整体
根据数据组织方式的不同,python的组合数据类型分为3类:序列类型、集合类型、映射类型。
1.序列类型:按一定顺序依次排列的一组数据,python中的序列支持双向索引,正向递增索引和反向递减索引
- 正向索引从左向右依次递增,第一个元素的索引为0,第二个元素的索引为1,以此类推,如下图所示:
- 反向索引从右向左依次递减,最右边的第一个元素的索引为-1,之后向左以此类推,如图所示:
-
python中常用的序列类型主要有3种:字符串(str)、列表(list)、元组(tuple)。
2.集合类型:python集合与数学中集合的概念一致,也具备确定性、互异性、无序性。
- 确定性:集合中的每个元素都是确定的
- 互异性:集合中的元素都是唯一的
- 无序性:集合中的元素是无序的,如果多个集合中的元素只是顺序不同,那么这些集合本质上是同一集合
python要求存入集合中的元素必须是不可变类型(如整型、浮点型、字符串类型、元组,而列表、字典、集合本身都是可变数据类型)
3.映射类型:映射类型以键值对的形式存储元素,键与值之间存在映射关系。
字典(dict)是python唯一的内置映射,字典的键必须遵循两个原则:
- 每个键只能对应一个值,且键唯一
- 键是不可变类型
5.2 列表
python利用内存中一段连续的空间存储列表,列表是python中最灵活的序列类型,它没有长度限制,可以包含任意类型的元素,且可以被修改
5.2.1 创建列表
创建列表既可以直接使用“[]”中括号创建,也可以用内置函数list()创建。
1. 使用中括号“[]”直接赋值的方式创建列表
list_one = [] #空列表
- []中包括的是列表元素,列表元素可以是整型、浮点型、字符串等基本类型,也可以是列表、元组、字典等组合类型,还可以是其他自定义类型
- 列表元素的类型可以相同也可以不相同
list_two = ['p','y','t','h','o','n']
list_three = [1,'a','&',2.3]
list_four = [1,'a','&',2.3,list_three]
2.使用list()函数创建列表
list()函数接收一个可迭代类型的数据,然后返回一个列表,例如:
list_two = list(1) #创建失败,因为int型不是可迭代类型
list_three = list('python') #字符串类型是可迭代类型
list_four = list([1,'python']) #列表类型是可迭代类型
什么是可迭代对象?
支持通过for...in语句迭代获取数据的对象就是可迭代对象,字符串、列表、集合、字典、文件类型的数据都是可迭代对象。
使用isinstance()函数可以判断目标是否为可迭代对象:
from collections.abc import Iterable
ls = [3,4,5]
print(isinstance(ls,Iterable))
运行结果:
5.2.2 访问列表元素
列表中的元素可以通过索引和切片两种方式访问,也可以在循环中依次访问。
1. 通过索引访问列表元素
语法格式:
list[n]
2. 通过切片的方式访问列表元素
用于获取列表中的部分元素,获取一个新列表,语法格式如下:
list[m:n:step]
以上语法格式表示:按步长step获取列表list中索引m~n的元素(不包括list[n])
- step默认为1
- m可以省略,省略表示从列表第一个元素开始
- n可以省略,省略表示切片到列表最后一个元素
案例:
my_list = ['p','y','t','h','o','n']
print(my_list[1:4:2]) #获取列表my_list中索引1-4的元素,步长为2
print(my_list[2:]) #获取列表中索引2-最后一个元素的元素
print(my_list[:3]) #获取列表中索引0-3的元素
print(my_list[:]) #获取列表中的所有元素
运行结果:
3. 在循环中依次访问列表中的元素
my_list = ['p','y','t','h','o','n']
for ls in my_list:
print(ls,end='')
5.2.3 向列表中添加元素
1. 列表的append()方法
用于在列表的末尾添加新元素
my_list = ['p','y','t','h','o','n']
my_list.append('java')
print(my_list)
2. 列表的extend()方法
用于在列表的末尾一次性添加另一个列表中的所有元素
my_list = ['p','y','t','h','o','n']
my_list.extend(['java','c'])
print(my_list)
3. insert()方法
将新元素插入到列表的指定索引位置。
my_list = ['p','y','t','h','o','n']
my_list.insert(1,'c')
print(my_list)
5.2.4 元素排序
:指将列表中的元素按照某种规则进行排序,python中对列表中元素进行排序的常用方法有:sort()、sorted()、reverse()方法。
1. sort()方法:按照指定顺序对列表中的元素进行排序,有序的元素会覆盖原来的列表元素,不产生新的列表。该方法的语法如下:
sort(key=None,reverse=False)
- key:指定排序规则,该参数可以是列表支持的函数,默认值为None
- reverse:指定列表元素的排序方式,该参数可以取值True或False,为True时表示降序排序,为False时表示升序排序
案例:
li_one = [6,2,5,3]
li_two = [7,3,5,4]
li_three = ['python','java','php']
#升序排序
li_one.sort()
#降序排序
li_two.sort(reverse=True)
#len()函数可以计算字符串的长度,按照列表中每个字符串元素的长度排序
li_three.sort(key=len)
print(li_one)
print(li_two)
print(li_three)
运行结果:
[2, 3, 5, 6]
[7, 5, 4, 3]
['php', 'java', 'python']
2. sorted()方法:用于升序排列列表中的元素,会返回升序排序后的新列表,不会改变原列表。
li_one = [4,3,2,1]
li_two = sorted(li_one)
print(li_one)
print(li_two)
运行结果:
[4, 3, 2, 1]
[1, 2, 3, 4]
3. reverse()方法:用于逆置列表,该方法不会产生新的列表。
li_one = ['a','b','c','d']
li_one.reverse()
print(li_one)
运行结果:
['d', 'c', 'b', 'a']
5.2.5 删除列表元素
1. del语句:删除列表中指定索引的列表元素或删除整个列表。
li_one = ['a','b','c','d']
del li_one[0]
print(li_one)
运行结果:
['b', 'c', 'd']
删除整个列表:
li_one = ['a','b','c','d']
del li_one
print(li_one)
运行结果:
2.remove()方法:移除列表中匹配到的第一个元素
li_one = ['a','b','c','c','d']
li_one.remove('c')
print(li_one)
运行结果:['a', 'b', 'c', 'd']
3.pop()方法:按索引删除列表元素,并返回被删除的元素,如果未指定索引,则删除列表中的最后一个元素
li_one = ['a','b','c','c','d']
print(li_one.pop())
print(li_one.pop(2))
print(li_one)
运行结果:
d
c
['a', 'b', 'c']
4. clear()方法:清空列表
names = [1,2,3]
names.clear()
print(names)
运行结果:[]
5.3 元组
使用()小括号来表示元组,元组中的元素用逗号分隔,它可以保存任意数量、任意类型的元素,但不能被修改,元组是不可变类型。
5.3.1 创建元组
1. 使用()小括号直接创建元组
t1 = ()
t2 = (1,)
t3 = (1,2,3)
t4 = (1,'c',('c',2))
注意:如果元组中只有一个元素,元素后的逗号不能省略。
2. 使用内置函数tuple()创建元组
#当函数的参数列表为空时创建空元组
t1 = tuple()
#向tuple()函数传入为可迭代对象可以创建非空元组
t2 = tuple([1,2,3])
t3 = tuple('python')
t4 = tuple(range(5))
5.2.2 访问元组中的元素
和列表一样,可以通过索引和切片访问元组中的元素,也可以在循环中遍历元组。
因为元组是不可变类型,所以不支持添加元素、删除元素、排序操作。
5.5 集合
集合可以存储任意数量、任意类型的元素,但集合中的元素无序且唯一,使用"{}"表示(创建)集合,集合中的元素以逗号分隔,集合本身是可变类型,但存入集合中的元素必须是不可变类型。
5.5.1 创建集合
1. 使用{}直接赋值的方式创建集合
#使用{}直接赋值的方式创建集合
s1 = {1}
s2 = {1,'b',(2,5)}
注意:使用这种方式不能创建空集合,空集合只能使用set()函数创建
2. 使用set()函数创建集合
向set()函数传入可迭代对象创建非空集合。
s1 = set([1,2,3])
s2 = set((2,3,4))
s3 = set('python')
s4 = set(range(5))
5.5.2 集合的常见操作
5.6 字典(可变数据类型)
字典中的每个元素都是 key:value形式的键值对,值可以是任意类型,键不能重复且必须是不可变类型,python中使用"{}"创建(表示)字典,字典中的元素用逗号分隔。
5.6.1 创建字典
1. 使用{}直接赋值的方式创建字典
d1 = {}
d2 = {'A':'123','B':'135','C':'680'}
d3 = {'A':123,12:'python'}
2. 使用dict()函数创建字典
d1 = dict()
d2 = dict({'A':'123','B':'135'})
5.6.2 字典的访问
1. 通过键访问对应值
语法格式:
字典遍历[键]
2. 通过get()方法,根据键访问字典中对应的值,若指定键不存在则返回默认值(default)
字典名.get(key[,default])
d2 = {'A':'123','B':'135','C':'680'}
print(d2.get('A'))
3. 访问字典中所有的键
字典名.keys()
4. 访问字典中所有的值
字典名.values
5. 访问字典所有的元素
字典名.items()
3,4,5返回的都是可迭代对象,利用循环可以遍历这些对象:
for 临时遍历 in 字典名.keys():
print(key)
5.6.3 添加字典元素
1.通过指定键赋值的方式
当字典中不存在某个键时,可以指定键的赋值添加字典元素:
字典变量[键] = 值
案例:
add_dict = {'name':'jack','age':23,'height':185}
add_dict['score'] = 98
print(add_dict)
运行结果:
{'name': 'jack', 'age': 23, 'height': 185, 'score': 98}
2. 使用字典的update()方法
add_dict = {'name':'jack','age':23,'height':185}
add_dict.update(score=98)
print(add_dict)
运行结果:
{'name': 'jack', 'age': 23, 'height': 185, 'score': 98}
5.6.4 修改字典元素
修改字典元素本质上还是通过键访问值,再对元素进行赋值。
add_dict = {'name':'jack','age':23,'height':185}
add_dict['name'] = 'jetty' #通过指定键赋值的方式修改字典元素
add_dict.update(age=24) #通过字典的update()方法修改字典元素
print(add_dict)
运行结果:
{'name': 'jetty', 'age': 24, 'height': 185}
5.6.5 字典元素的删除
1. 字典的pop()方法
描述:根据指定的键删除字典中的字典元素,删除成功,则返回删除元素的值。
add_dict = {'name':'jack','age':23,'height':185}
print(add_dict.pop('name'))
print(add_dict)
运行结果:
2. 字典的popitem()方法
描述:随机删除字典中的元素,删除成功会返回被删除的元素
add_dict = {'name':'jack','age':23,'height':185}
print(add_dict.popitem())
print(add_dict)
运行结果:
('height', 185)
{'name': 'jack', 'age': 23}
3. 字典的clear()方法
描述:清空字典中的元素
add_dict = {'name':'jack','age':23,'height':185}
add_dict.clear()
print(add_dict)
运行结果:
5.8 组合数据类型应用运算符
1. “+”运算符
python中的字符串、列表、元组支持加号运算符,与数字类型不同,组合数据类型是进行数据的拼接而不是数值的累加
str1 = "hello"
str2 = "world"
print(str1 + str2)
list1 = [1,2,3]
list2 = [4,5,6]
print(list1 + list2)
tuple1 = (1,2,3)
tuple2 = (4,5,6)
print(tuple1 + tuple2)
运行结果:
2. “*”运算符
字符串、列表、元组可以与整数进行乘法运算,运算结果为原数据的整数倍拼接。
str1 = "hello"
print(str1 * 2)
list1 = [1,2,3]
print(list1 * 3)
tuple1 = (1,2,3)
print(tuple1 * 4)
运算结果:
3. “in”和“not in”运算符
这两个称为成员运算符,用于判断某个元素是否属于某个变量,python的字符串、列表、元组、集合和字典都支持成员运算符。
list_one = [1,2,3]
print(1 in list_one)
print(1 not in list_one)
运算结果:
6. 函数
6.1 函数概述
函数是已编写好的,实现某种功能的代码块。
使用函数可以使程序模块化,减少了冗余代码又让程序结果更为清晰,提供编程效率,方便后期的维护和扩展。
6.2 函数的定义和调用
6.2.1 定义函数
使用def关键字来定义函数,其语法格式为:
def 函数名([形参表]):
函数体
[return语句]
说明:
- def:函数的开始标志
- 函数名:函数的唯一标识
- 形参表:可以包含一个或多个形参,也可以为空,用于接收传给函数的实参
- 冒号:函数体的开始标志
- 函数体:实现函数功能的具体代码
- return语句:返回函数的处理结果给调用者,是函数的结束标志,如果函数没有返回值,则可以省略return语句,可以返回一个值也可以返回多个值,返回多个值时,值和值之间用逗号分隔,并且这些值将会用元组保存。
案例:定义一个计算两个数之和的无参函数
def add():
result = 11 + 22
return result
案例:定义一个计算两个数之和的有参函数
def add(a,b):
result = a + b
return result
6.2.2 调用函数
定义函数后函数不会立刻执行,需要在程序中调用才会执行,调用函数的方式如下:
函数名([实参表])
例如调用6.2.1中定义的两个函数
add()
add(a,b)
6.2.3 函数的嵌套调用
函数内部也可以调用其他函数,这被称为函数的嵌套调用。
6.2.4 嵌套定义函数
在定义一个函数时,可以在其内部又定义另一个函数,此时嵌套的函数称为外层函数,被嵌套的函数称为内层函数。
def add_modify(a,b):
result = a + b
print(result)
def test():
print("我是内层函数")
add_modify(10,20)
运行结果:
为什么内层函数没有执行呢?
因为函数外部无法直接调用内层函数,需要在外层函数中调用内层函数,内层函数才会执行。
def add_modify(a,b):
result = a + b
print(result)
def test():
print("我是内层函数")
test()
add_modify(10,20)
运行结果:
6.3 函数参数的传递
函数的参数传递:调用函数时,将实际参数传递给形式参数的过程。
函数的参数传递可以分为:
- 位置参数的传递
- 关键字参数的传递
- 默认参数的传递
- 参数的打包与解包
- 混合传递
6.3.1 位置参数的传递
位置参数传递就是:调用函数时,将实参按位置依次传给形参。
位置参数传递就如前几节所讲的一样,和C,Java这些编程语言的函数传参一样,这里不在过多赘述。
6.3.2 关键字参数的传递
关键字参数传递就是:通过 形参=实参 的格式,将实参 按形参名 传递给形参。
例如:
def connect(ip,port):
print(f"设备{ip}:{port}连接!")
connect(ip="127.0.0.1",port=8080)
运行结果:
6.3.3 默认参数的传递
默认参数的传递就是:定义函数的时候,给形参设置默认值
设置了默认值的形参,如果没有传递实参给它,它本身就具有该默认值
例如:
def connect(ip,port=3306):
print(f"设备{ip}:{port}连接!")
connect(ip="127.0.0.1")
运行结果:
6.3.4 参数的打包和解包
python中函数支持将实参以打包和解包的形式传递给形参。
1. 打包
如果函数在定义时无法确定要接收多少个数据,那么可以在定义函数时,在形参前添加“*”或“**”
- 如果在函数的形参前添加“*”,则它可以接收以元组形式打包的多个值
- 如果在函数的形参前面添加“**”,则它可以接收以字典形式打包的多个值
- 概括总结:打包其实就是定义函数时,在形参前添加“*”或“**”,将来调用该函数时,将多个实参以元组或字典的形式打包后传递给形参。
案例1:
def test(*args):
print(args)
#在调用test函数时,传入多个实参,多个实参会被打包后传递给形参
test(11,22,33,44,55)
运行结果:
案例2:
def test(**args):
print(args)
test(a=11,b=22,c=33,d=44,e=55)
运行结果:
注意:
- 带“*”或“**”的形参名建议以*args和**kwargs命名
- 如果函数没有接收到任何数据,参数*args和**kwargs为空,即为空元组或空字典。
2. 解包
调用函数时,传递的实参是元组类型的数据,可以使用“*”将元组解包成多个值,并将每个值按位置参数传递的方式赋值给形参
调用函数时,传递的实参是字典类型的数据,可以使用“**”将字典解包成多个键值对,并将每个值按关键字参数传递的方式赋值给和键名对应的实参
案例1:
#定义一个带有5个形参的函数test()
def test(a,b,c,d,e):
print(a,b,c,d,e)
#在调用函数test时,传入一个包含五个元素的元组,并使用“*”解包该元组
nums = (11,22,33,44,55)
test(*nums)
运行结果:
案例2:
#定义一个带有5个形参的函数test()
def test(a,b,c,d,e):
print(a,b,c,d,e)
#在调用函数test时,传入一个包含五个元素的字典,并使用“**”解包该字典
nums = {"a":11,"b":22,"c":33,"d":44,"e":55}
test(**nums)
6.3.5 混合传递
前面介绍的参数传递方式在定义函数或调用函数时可以混合使用,但需要遵循一定的优先级规则。
参数传递方式的优先级从高到低依次为为:
- 位置参数传递
- 关键字参数传递
- 默认参数传递
- 打包传递
定义函数时,带有默认值的参数必须位于普通参数之后;带有“*”标识的参数必须位于带有默认值的参数之后,带有“**”标识的参数必须位于带有“*”标识的参数之后。
例如:
def test(a,b,c=33,*args,**kwargs):
print(a,b,c,args,kwargs)
#该函数接收到实参1和实参2,这两个实参被普通参数a和b接收,剩余参数没有接收到实参,都使用默认值
test(1,2)
#该函数接收到实参1-实参3,前三个实参被普通参数a,b,和默认值参数c接收,剩余形参没有接收到实参,使用默认值
test(1,2,3)
#该函数接收到实参1-实参4,前4个实参被形参a,b,c,*args接收,*kwargs没有接收到实参,使用默认值
test(1,2,3,4)
#所有的实参被相应的形参接收
test(1,2,3,4,e=5)
运行结果:
6.4 变量作用域
变量的有效范围称为该变量的作用域
6.5.1 局部变量和全局变量
根据作用域的不同,变量分为局部变量和全局变量。
1. 局部变量
局部变量就是在函数内定义的变量,局部变量只能在函数内部使用,函数执行结束后局部变量就会被释放,此时无法访问。
注意:不同函数内部可以使用同名的局部变量,它们相互独立,互不影响。
2. 全局变量
全局变量就是定义在函数外的变量,它可以在整个程序的范围内起作用,不会受函数访问的影响。
注意:全局变量在函数内部只能访问,而不能直接修改
例如,这里的函数内部的变量number被视为局部变量,而不是对全局变量number的值进行修改。
number = 10
def test():
number = 5
print(number)
test()
print(number)
运行结果:
LEGB原则
L(Local):局部作用域,例如局部变量和形参起作用的区域
E(Enclosing):嵌套作用域
G(Global):全局作用域
B:内置作用域,例如内置模块声明的变量生效的区域
Python搜索变量时会按照L-E-G-B的顺序在四个区域中搜索变量,如果搜索到变量则停止搜索,使用搜索到的变量,如果四个区域都没有搜索到,则程序抛出异常。
6.5.2 global和nonlocal关键字
函数内部无法直接修改全局变量 或 在嵌套函数的外层函数声明的变量,此时可以使用global或nonlocal关键字修饰变量,以间接修改这些变量
1. global关键字
使用global关键字可以将局部变量声明为全局变量,然后就可以在函数内部修改全局变量了,语法为:
global 变量
案例:
#定义全局变量
number = 10
#在函数内修改全局变量的值
def test():
global number
number = 5
#在函数外访问全局变量number,看值是否修改
test()
print(number)
运行结果:
2. nonlocal关键字
使用nonlocal关键字可以在内层函数中修改外层函数中声明的变量,其语法:
nonlocal 变量
案例:
def test():
number = 10
def test_in():
nonlocal number
number = 20
test_in()
print(number)
test()
如果不使用nonlocal关键字结果如何?
def test():
number = 10
def test_in():
number = 20
test_in()
print(number)
test()
运行结果:
6.7 特殊形式的函数
6.7.1 递归函数
函数在定义时可以直接或间接地调用其他函数,如果函数内部调用了它自己,则这个函数被称为递归函数。
递归函数在定义时,需要满足2个基本条件:一个是递归公式,一个是边界条件。其中递归公式是求解原问题或相似子问题的结构;边界条件是最小化的子问题,也是递归终止的条件。
递归函数的执行可以分为以下两个阶段:
- 递推:递归本次的执行都基于上一次的执行结果
- 回溯:遇到终止条件时,则沿着递推往回一级一级地把值返回来
递归函数的一般定义格式如下:
def 函数名([形参表]):
if 边界条件:
return 结果
else:
return 递归公式
案例:递归最经典的应用就是阶乘,在数学中,求正整数n!(n的阶乘)问题根据n的取值可以分为以下两种情况:
- 当n=1时,所得结果为1
- 当n>1时,所得结果为n*(n-1)
那么利用递归求解阶乘时,n=1是边界条件,n*(n-1)!是递归公式,代码如下:
def func(num):
if num == 1:
return 1
else:
return num * func(num - 1)
num = int(input("输入一个整数:"))
result = func(num)
print(f"{num}!=%d" % result)
运行结果:
6.7.2 匿名函数
匿名函数就是没有函数名的函数。
Python中使用lambda关键字定义匿名函数,其语法格式如下:
lambda <形式参数列表> : <表达式>
匿名函数和普通函数的区别:
- 普通函数在定义时有函数名,而匿名函数没有函数名
- 普通函数的函数体中可以有多条语句,而匿名函数的函数体只能是一个表达式
- 普通函数可以实现比较复杂的功能,而匿名函数可以实现的功能比较简单
- 普通函数可以被其他程序调用,而匿名函数不能
定义好的匿名函数不能直接使用,最好使用一个变量保存它,后面就可以使用这个变量来调用匿名函数。
案例:
temp = lambda x : pow(x,2)
print(temp(10))
运行结果:
7.文件与数据格式化
8. 面向对象
8.1 面向对象概述
面向对象和面向过程不同,它关注的不是解决问题的过程,基于面向对象思想开发程序时会先分析问题,从中提炼出多个对象,将不同对象的特征和行为进行封装,之后通过对象的行为来解决问题。
8.2 类与对象
8.2.1 类的定义
类是现实中具有相同特征和行为的事物的抽象。
python中使用class关键字来定义一个类,语法如下:
class 类名:
属性名 = 属性值
def 方法名(self):
方法体
在类中可以定义对象的属性和方法,属性和方法类似于前面所讲的变量和函数,但类中方法形参表的第一个参数是self,它是一个指代对象的默认参数。
案例:
class Car:
wheels = 4
def drive(self):
print("行驶")
8.2.2 对象的创建和使用
在程序中要想使用类则需要先创建一个类的对象。
创建对象的语法如下:
对象名 = 类名()
对象的使用本质上就是访问其属性和调用其方法,访问属性或调用方法的语法如下:
对象名.属性名
对象名.方法名()
案例:
#定义一个类Car
class Car:
wheels = 4
def drive(self):
print("行驶")
#创建Car类的对象
car = Car()
#使用对象
print(car.wheels)
car.drive()
运行结果:
8.3 类的成员
类的成员包括属性和方法,默认它们可以在类的外部被访问或调用,但考虑到数据安全,需要将其设置为私有成员,限制类的外部对其进行访问或调用。
- 属性
- 方法
8.3.1 类的属性
类的属性按声明方式可以分为:类属性和实例属性。
1. 类属性
在类中方法外定义的属性,类属性可以通过类或对象进行访问,但只能通过类名修改类属性的值,而不能通过对象修改类属性的值,类属性属于类。
案例:
#定义一个类Car
class Car:
wheels = 4 #类属性
def drive(self):
print("行驶")
car = Car()
print(Car.wheels) #通过类名访问类属性
print(car.wheels) #通过对象名访问类属性
Car.wheels = 3 #通过类名修改类属性wheels
print(Car.wheels)
print(car.wheels)
car.wheels = 4 #通过对象名修改类属性wheels,这里其实是动态添加了实例属性wheels
print(Car.wheels)
print(car.wheels)
运行结果:
2. 实例属性
在类中方法中定义的属性,在程序中(类的外部),实例属性属于对象,只能用对象访问或修改
实例属性一般在构造方法中定义,实例属性的定义和使用要以self为前缀。
(1)访问实例属性
实例属性只能通过对象名进行访问,例如:
#定义一个类Car
class Car:
def drive(self):
self.wheels = 4 #使用self定义实例属性wheels
car = Car()
car.drive()
print(car.wheels) #通过对象名访问
print(Car.wheels) #通过类名访问
运行结果:
(2)修改实例属性
实例属性通过对象进行修改
#定义一个类Car
class Car:
def drive(self):
self.wheels = 4 #定义实例属性
car = Car()
car.drive()
car.wheels = 3
print(car.wheels)
运行结果:
(3)动态添加实例属性
python支持在类外使用对象动态添加实例属性,例如:
#定义一个类Car
class Car:
def drive(self):
self.wheels = 4 #定义实例属性
car = Car()
car.drive()
car.wheels = 3
print(car.wheels)
#动态添加实例属性color
car.color = "红色"
print(car.color)
8.3.2 类的方法
类的方法按定义方式和用途可以分为:实例方法、类方法、静态方法。
1. 类的实例方法
实例方法和函数一样,但定义在类中,第一个形参为self,这个self参数代表对象本身,它会自动接收调用该方法的对象。
实例方法只能通过对象调用,例如:
#定义一个类Car
class Car:
def drive(self): #定义实例方法
print("我是实例方法")
car = Car()
car.drive() #通过对象调用实例方法
Car.drive() #通过类调用实例方法
运行结果:
2. 类方法
定义在类中,使用@classmethod装饰器修饰的方法,类方法中形参表的第一个参数为cls,代表类本身,它在类方法被调用时自动接收调用该方法的类。
类方法可以通过类或对象调用,例如:
#定义一个类Car
class Car:
@classmethod
def stop(cls):
print("我是类方法")
car = Car()
car.stop() #通过对象调用类方法
Car.stop() #通过类调用类方法
运行结果:
在类方法中可以使用cls访问和修改类属性的值,例如:
#定义一个类Car
class Car:
wheels = 3 #定义类属性
@classmethod #定义类方法
def stop(cls):
print(cls.wheels) #使用cls访问类属性
cls.wheels = 4 #使用cls修改类属性
print(cls.wheels)
car = Car()
car.stop() #通过对象调用类方法
运行结果:
3. 类的静态方法
定义在类中,使用@staticmethod装饰器修饰的方法,和实例方法跟类方法比,静态方法没有任何默认参数,它适用于与类无关的操作,或无须使用类成员的操作,常见于一些工具类中。
例如:
#定义一个类Car
class Car:
@staticmethod
def test():
print("我是静态方法")
静态方法可以通过类或对象调用,例如:
#定义一个类Car
class Car:
@staticmethod
def test():
print("我是静态方法")
car = Car()
car.test()
Car.test()
在静态方法中,不能直接访问属性或方法,但可以使用类名访问类属性或调用类方法,例如:
#定义一个类Car
class Car:
wheels = 3 #类属性
@staticmethod
def test():
print("我是静态方法")
print(f"类属性的值={Car.wheels}") #在静态方法中访问类属性
car = Car()
car.test()
运行结果:
8.3.3 类的私有成员
类的成员默认为公有成员,可以在类外部通过类或对象随便访问,但这样并不安全,这时可以将公有成员改为私有成员。
在类成员名前添加两个下划线来私有化,如下:
__属性名
__方法名
例如:
class Car:
__wheels = 4 #私有属性
def __drive(self): #私有方法
print("行驶")
类的私有成员在类内部可以直接访问,在类外部不能直接访问,需要通过调用类的公有方法来访问。
class Car:
__wheels = 4 #私有的类属性
def __drive(self): #私有方法
print("行驶")
def test(self): #在公有方法中访问私有成员
print(f"轿车有{self.__wheels}个轮子")
self.__drive()
car = Car()
car.test()
8.4 类中的特殊方法
类中还有两个特殊的方法,这两个方法都是系统内置的方法:
- __init__()构造方法
- __del__()析构方法
8.4.1 构造方法
作用:在创建对象时对对象的属性进行初始化。(在创建对象时系统会自动调用类的构造方法)
每个类中都有一个构造方法__init__(),如果类中显式定义了构造方法,那么创建对象时调用的是显式定义的构造方法;否则调用默认的构造方法。
__init__()方法可以分为无参构造方法和有参构造方法
(1)使用无参构造方法创建对象时,所有对象的属性都有相同的初始值
(2)使用有参构造方法创建对象时,所有对象的属性可以有不同的初始值
对于(1)的例子:
class Car:
def __init__(self): #无参构造方法
self.color = "红色"
def drive(self):
print(f"车的颜色为:{self.color}")
car_one = Car()
car_one.drive()
car_two = Car()
car_two.drive()
运行结果:
对于(2)的例子:
class Car:
def __init__(self,color): #有参构造方法
self.color = color #给实例属性赋值为color
def drive(self):
print(f"车的颜色为:{self.color}")
car_one = Car("红色")
car_one.drive()
car_two = Car("蓝色")
car_two.drive()
运行结果:
8.4.2 析构方法
作用:销毁对象时系统自动调用该方法。
每个类中默认都有一个析构方法。
如果一个类中显式定义了析构方法,那么销毁该类的对象时调用的时显式定义的析构方法;如果一个类中没有定义析构方法,那么销毁该类的对象时会调用默认的析构方法。
案例:
class Car:
def __init__(self):
self.color = "蓝色"
print("构造方法执行,对象被创建")
def __del__(self):
print("析构方法执行,对象被销毁")
car = Car()
print(car.color)
del car #使用del语句删除对象的引用,此时会执行析构方法
print(car.color) #这行代码因为已经销毁了对象,在使用car对象访问属性会报错
8.6 封装
封装是面向对象的重要特征之一,它的基本思想是对外隐藏类的实现细节,提供用于访问类成员的公开接口。
为了契合封装的思想,定义类时要满足以下两点:
- 将属性私有化
- 定义两个供外界调用的公有方法,分别用于设置和获取私有属性的值。
案例:
class Person:
def __init__(self,name):
self.name = name
self.__age = 1 #将实例属性age私有化
#设置私有属性值的方法
def set_age(self,new_age):
if 0< new_age <= 120:
self.__age = new_age
#获取私有属性值的方法
def get_age(self):
return self.__age
person = Person("小明")
person.set_age(18)
print(f"年龄为{person.get_age()}岁")
运行结果:
8.7 继承
继承是面向对象的重要特征之一,它主要用于描述类与类之间的关系,在原有类的基础上扩展原有类的功能。如果类与类之间具有继承关系,被继承的类称为父类或基类,继承其他类的类称为子类或派生类,子类会自动拥有父类的公有成员,但不会拥有父类的私有成员,也不能访问父类的私有成员。
8.7.1 单继承
单继承就是子类只继承一个父类,其语法格式为:
class 子类名(父类名):
下面定义一个Cat类和一个继承Cat类的折耳猫类ScottishFold:
class Cat(object):
def __init__(self,color):
self.color = color
def walk(self):
print("猫走路")
#定义继承Cat类的折耳猫类
class ScottishFold(Cat):
pass #关键字pass用于表示一个空的代码块
fold = ScottishFold("灰色") #创建子类的对象
print(f"{fold.color}的折耳猫") #用子类对象调用从父类继承来的实例属性
fold.walk() #用子类对象调用从父类继承来的实例方法
运行结果:
8.7.2 多继承
程序中的一个类可以继承多个类,也自动拥有所有父类的公有成员,多继承的语法格式为:
class 子类名(父类名1,父类名2,...)
案例:
#定义一个表示房子的类
class House(object):
def live(self):
print("供人居住")
#定义一个表示汽车的类
class Car:
def drive(self):
print("行驶")
#定义一个表示房车的类,继承House类和Car类
class HouseCar(House,Car):
pass
#定义房车对象
house_car = HouseCar()
house_car.live()
house_car.drive()
如果继承的多个父类中有同名的方法怎么办?
如果子类继承的多个父类是平行关系的类,那么子类先继承哪个类,就会先调用哪个类的方法。
例如在上述代码的House和Car类中添加一个test()方法:
#定义一个表示房子的类
class House(object):
def live(self):
print("供人居住")
def test(self):
print("House类的test方法")
#定义一个表示汽车的类
class Car:
def drive(self):
print("行驶")
def test(self):
print("Car类的test方法")
#定义一个表示房车的类,继承House类和Car类
class HouseCar(House,Car):
pass
#定义房车对象
house_car = HouseCar()
house_car.live()
house_car.drive()
#用房车对象调用test方法试试
house_car.test()
8.7.3 重写
子类会原封不同地继承父类的方法,但子类有时需要按自己的需求对继承来的方法进行调整。
此时可以在子类中重写从父类继承来的方法。
python中重写父类方法的方式:在子类中定义和父类方法同名的方法,然后重新编写方法体即可。
案例:
#Person类
class Person(object):
def say_hello(self):
print("打招呼")
#定义Person的子类
class Chinese(Person):
def say_hello(self): #重写父类中的say_hello方法
print("吃了吗")
chinese = Chinese()
chinese.say_hello()
运行结果:
子类重写了从父类继承来的方法后,无法直接访问父类的同名方法,此时可以使用super()函数调用父类中被重写的方法。
案例:
#Person类
class Person(object):
def say_hello(self):
print("打招呼")
#定义Person的子类
class Chinese(Person):
def say_hello(self): #调用父类中被重写的方法
super().say_hello()
print("吃了吗")
chinese = Chinese()
chinese.say_hello()
运行结果:
8.8 多态
多态是面向对象的重要特征,它的表现形式是:让不同类的同一方法,可以通过相同的接口调用,表现出不同的行为。
例如:定义一个Cat类和一个Dog类,两个类中都定义shout()方法:
class Cat:
def shout(self):
print("喵喵")
class Dog:
def shout(self):
print("汪汪")
#定义一个接口,通过这个接口调用Cat类和Dog类中的shout()方法
def shout(obj):
obj.shout()
cat = Cat()
dog = Dog()
shout(cat)
shout(dog)
在函数shout(obj)中,参数obj代表一个对象,可以是Cat类的实例,也可以是Dog类的实例,甚至是其他类的实例。通过传递不同的对象给shout(obj)函数,可以实现对不同类中相同名称方法的调用。
利用多态这一特性编写代码不会影响类的内部设计,但可以提高代码的兼容性,让代码的调度更加灵活。
8.9 运算符重载
运算符重载:赋予了内置运算符新的功能,使内置运算符可以适应更多的数据类型。
理解:当定义一个类时,在这个类中重写object基类中内置的有关运算符的特殊方法,那么该特殊方法对应的运算符将支持对该类的实例进行运算。
object基类中提供了一些特殊方法及对应的运算符如下所示:
下面定义一个表示计算器的Calculator类,并重写该类的特殊方法__add__()、__sub__()、__mul__()、__truediv__(),使运算符+、-、*、/能够支持针对Calculator对象的运算:
class Calculator(object):
def __init__(self,number): #记录数值
self.number = number
def __add__(self, other): #重载运算符+
self.number = self.number + other
return self.number
def __sub__(self, other): #重载运算符-
self.number = self.number - other
return self.number
def __mul__(self, other): #重载运算符*
self.number = self.number * other
return self.number
def __truediv__(self, other): #重载运算符/
self.number = self.number / other
return self.number
#创建Calculator类的对象,让该对象使用运算符与一个数分别执行加减乘除操作
calculator = Calculator(10)
print(calculator + 5)
print(calculator - 5)
print(calculator * 5)
print(calculator / 5)
运行结果:
对运算符重载后,自定义类Calculator也可以进行四则运算了。
9. 异常
9.1 异常概述
9.1.1 认识异常
程序运行出现异常时,如果程序中没有设置异常处理的代码,那么解释器会使用系统的默认方式处理异常,即返回异常信息,终止程序。
异常信息中通常包含异常代码的行数、异常类型和异常的描述信息。
以四则运算为例来看异常信息的例子:
x = 10
print(x/0)
以上异常信息的2~3 行指出了异常所在的行和此行的代码,第四行说明了本次异常的类型和异常描述。
9.1.2 异常的类型
python程序运行出错时产生的每个异常类型都对应一个类,程序运行时出现的异常大多继承Exception类,Exception类又继承异常类的基类BaseException,通过下面一张图来说明Python中异常类的继承关系:
Exception类内置了许多常见的异常,下面介绍几种常见的异常。
1. NameError
NameError异常是程序中使用了未定义的变量,就会引发此异常。
案例:
print(x)
2. IndexError
IndexError异常是索引越界异常。
案例:
ls = [1,2]
print(ls[2])
3. AttributeError
AttributeError异常是使用对象访问不存在的属性,就会发生此异常。
案例:
class Car(object):
pass
car = Car()
car.color = "黑色"
car.brand = "五菱"
print(car.color)
print(car.brand)
print(car.name) #访问不存在的属性
4. FileNotFoundError
FileNotFoundError异常是未找到指定文件或目录时,会发生此异常。
file = open("test.txt")
9.2 异常捕获语句
python程序在运行时,出现异常会直接崩溃,然后打印异常信息,系统默认这样处理异常,而这种系统默认处理异常的方式并不友好。
9.2.1 使用try-except语句捕获异常
语法格式如下:
try:
可能出错的代码
except [异常类型 [as error]]: #将捕获到的异常对象赋值给error
捕获异常后,处理异常的代码
说明:
- 在except子句中可以指定异常类型,如果指定了异常类型,该子句就只会对指定的异常类型进行处理,否则处理try语句捕获的所有异常
- except子句中的as关键字用于将捕获到的异常对象赋值给error,error可以记录异常信息。
执行流程:
- 先执行try子句中可能出错的代码。
- 如果try子句中没有出现异常,就忽略except子句继续向下执行程序;
- 如果try子句中出现异常,就不执行try子句中剩下的代码,转而执行except子句:如果出现的异常类型和except子句中指定的异常类型匹配,使用error记录异常信息,执行except中的代码,否则按系统默认处理异常的方式终止程序。
try-except语句可以捕获与处理程序的单个、多个或全部异常,下面一一介绍
1. 捕获单个异常
在except之后指定要捕获的单个异常类型即可,例如:
num_one = int(input("请输入被除数:"))
num_two = int(input("请输入除数:"))
try:
print("结果为",num_one/num_two)
except ZeroDivisionError as error:
print("出错了,原因:",error)
2. 捕获多个异常
在except之后以元组的形式指定多个异常类型,例如:
num_one = int(input("请输入被除数:"))
num_two = int(input("请输入除数:"))
try:
print("结果为",num_one/num_two)
except (ZeroDivisionError,ValueError) as error:
print("出错了,原因:",error)
3. 捕获程序中的全部异常
不指定except子句中的异常类型或指定异常类型为Except。
注意:如果省略异常类型,except子句中无法获取异常的具体信息,例如:
num_one = int(input("请输入被除数:"))
num_two = int(input("请输入除数:"))
try:
print("结果为",num_one/num_two)
except Exception as error:
print("出错了,原因:",error)
9.2.2 在异常结构中加else子句
语法结构如下:
try:
可能出错的代码
except [异常类型 [as error]]: #将捕获到的异常对象赋值给error
捕获到异常后,处理异常的代码
else:
没有捕获到异常时,执行的代码
以上结构中,如果try子句中的代码没有出现异常,程序就会执行else子句中的代码。
9.2.3 在异常结构中加finally子句
语法结构如下:
try:
可能出错的代码
except [异常类型 [as error]]: #将捕获到的异常对象赋值给error
捕获到异常后,处理异常的代码
finally:
一定执行的代码
上述结构中,无论try子句中的代码有没有出现异常,finally子句都会执行,基于此特性,在实际应用中,finally子句多用于预设资源的清理操作,如关闭文件、关闭网络连接、关闭数据库连接等。
9.3 抛出异常
python程序中,异常不仅可以自动触发,还可以使用raise语句和assert语句主动触发。
9.3.1 使用raise语句抛出异常
raise语句抛出异常的语法如下:
raise 异常类 #格式1:使用异常类的类名抛出指定的异常
raise 异常类对象 #格式2:使用异常类的对象抛出指定异常
raise #格式3:重新抛出刚刚发生的异常
说明:
- 第一种格式会隐式创建一个该异常类型的对象
- 第2种格式是最常见的形式,它会直接提供一个异常类型的对象
1. 使用异常类抛出异常
raise IndexError
2. 使用异常类的对象抛出异常
raise IndexError()
技巧:在创建异常类的对象时,还可以通过字符串指定异常的具体信息:
raise IndexError("索引越界访问")
3. 重新抛出异常
try:
raise IndexError("索引下标越界")
except:
raise
9.3.2 使用assert语句抛出异常
assert语句又叫断言语句,其语法格式如下:
assert 表达式 [,异常信息]
说明:
- aseert后面 跟一个表达式,表达式的值为True时不做任何操作,为False时触发AssertionError异常
- 表达式之后可以用字符串来描述异常信息
作用:assert语句可以帮助开发者在开发阶段调试程序,以保证程序的正确运行。
案例:使用assert语句判断用户输入的除数是否为0。
num_one = int(input("输入被除数:"))
num_two = int(input("输入除数:"))
assert num_two != 0,"除数不能为0" #assert语句判断除数不等于0
result = num_one/num_two
print(num_one,'/',num_two,'=',result)
9.3.3 异常的传递
如果程序中的异常没有被处理,默认情况下会将该异常传递到上一级,如果上一级仍然没有处理异常,就继续向上传递,直到异常被处理或程序崩溃。
通过一个计算正方形面积的示例来演示异常的传递:
def get_width(): #计算正方形边长的函数
print("get_width开始执行")
num = int(input("输入除数:"))
width_len = 10 / num
print("get_width执行结束")
return width_len
def calc_area(): #计算正方形的面积
print("calc_area开始执行")
width_len = get_width() #调用了get_width()函数,获取正方形的边长
print("calc_area执行结束")
return width_len * width_len
def show_area(): #输出正方形的面积
try:
print("show_area开始执行")
area_val = calc_area() #调用了calc_area()函数,获取正方形的面积
print(f"正方形的面积是:{area_val}")
print("show_area执行结束")
except ZeroDivisionError as error:
print(f"捕获到异常:{error}")
show_area() #程序入口
当用户输入的除数为0时,那么程序会出现ZeroDivisionError异常
- 此时,get_width()函数中没有捕获异常的语句,所以get_width()函数中引发的异常会向上传递到calc_area()函数
- calc_area()函数中也没有捕获异常的语句,继续将异常向上传递到show_area()函数
- show_area()函数中设置了try-except语句来处理异常,它在接收到calc_area()函数传递的异常后,会通过try-except语句处理异常
运行结果:
9.4 自定义异常
有时开发人员需要自定义异常类,以满足程序的需要。
例如,在设计用户注册账户功能时,需要限定用户名或密码等信息的类型和长度。
自定义异常的方法比较简单:只需要定义一个继承 Exception类或Exception子类 的类即可,这个类一般以Error结尾。
案例:通过用户注册的密码长度限制来演示自定义异常
class ShortInputError(Exception):
"""自定义异常类"""
def __init__(self,length,atleast):
self.length = length #输入的密码长度
self.atleast = atleast #限制的密码长度
try:
text = input("输入密码:")
if len(text) < 3: #如果输入密码的长度小于3,那么就抛出ShortInputError异常
raise ShortInputError(len(text),3)
except ShortInputError as error:
print("ShortInputError:输入的长度是%d,长度至少是%d" %
(error.length,error.atleast))
else:
print("密码设置成功")
运行结果:
更多推荐
所有评论(0)