基础语法
字面量
- 什么是字面量:
- 在代码中,被写下来的固定的值,称为字面量
| 类型 | 描述 | 说明 |
| — | — | — |
| 数组(Number) | 整数 int | 整数 |
| | 浮点型 float | 浮点数 |
| | 复数 complex | 复数 如 4+3j 以j结尾标识复数 |
| | 布尔 | True Flase 1 0 |
| 字符串 String | 描述文本的一种数据类型 | 又称文本,由任意数量的字符如中文,英文各类符号,数字等组成。Python中字符串需要用双引号包围起来,被引号包围的都是字符串 |
| 列表 List | 有序的可变序列 | Python中使用最频繁的数据类型,可有序记录一堆数据 |
| 元组 Tuple | 有序的不可变序列 | 有序记录一堆不可变的Python数据集合 |
| 集合 Set | 无序不重复集合 | 可无序记录一堆不重复的Python数据集合 |
| 字典 Dictionary | 无序Key-Value集合 | 可无序记录一堆Key-Value型的Python数据集合 |
- 在代码中,被写下来的固定的值,称为字面量
注释
- 单行注释,以#开头,#右边的所有文字当做说明,而不是真正要执行的程序,起辅助作用,注意:#号和注释内容建议以一个空格隔开。
- 多行注释:以一对三个双引号引起来(“”" 注释内容,支持换行"“”)来解释说明一段代码的作用的使用方法。
变量
- 在程序运行时,能存储计算结果或者能表示值的抽象概念
- 变量定义格式 变量名称 = 变量值
- 每一个变量都有自己的名称,称之为变量名,也就是变量本身
- 赋值,表示将等号右侧的值,赋予左侧的值
- 每一个变量都有自己存储的值,称之为变量值
数据类型
可以使用type() 查看变量类型 通过type(变量)是查看变量的类型还是数据的类型???
变量存储的数据类型,因为变量无类型,但是存储的数据有
- string
- int
- float
数据类型转换
数据类型之间,在特定的场景下,是可以相互转换的,如字符串转数字,数字转字符串等。
为什么要转换类型:
- 从文件中读取数字,默认是字符串,我们需要转成数字类型。
- input()语句,默认结果是字符串,如需要数字也需要转换。
- 将数字转成字符串用写出到外部系统
常见的转成语句
语句(函数) | 说明 |
---|---|
int(x) | 将x转成一个整型 |
float(x) | 将x转成一个浮点型 |
str(x) | 将对象x转成字符串 |
注意:这三个语句,都是带有结果的(返回值)我们可以用print直接输出,或者用变量存储结果值。
# 将数字变成字符串
num_str = str(11)
print(type(num_str), num_str) # 结果<class 'str'> 11# 浮点数转字符串
float_str = str(11.11)
print(type(flost_str), flost_str) # 结果<class 'str'> 11.11# 将字符串转成数字
num = int("11")
print(type(num), num) # 结果<class 'int'> 11
注意:int("[字符或者中文]") int("11.11") 将会报错 [ValueError: invalid literal for int() with xxxx]# 将字符串转成float
num_float = float("11.11")
print(type(num_float), num_float) # 结果<class 'float'> 11.11
注意:float("11") 将输入 11.0# 整数转浮点数
float_num = float("11")
输出11.0# 浮点数转整型(会丢失精度)
int_num = int(11.11)
输入 11
标识符
- 变量名字
- 方法的名字
- 类的名字
- 等等
这些名字,统一称之为标识符,用来做内容的标识;
python,标识符命名规则:
- 内容限定
- 标识符名称中 只允许出现,这4类元素,其余内容都不被允许
- 英文
- 中文 (不推荐)
- 数字 (数字不可以开头)
- 下划线( _ )
- 标识符名称中 只允许出现,这4类元素,其余内容都不被允许
- 大小写敏感
- 不可使用关键字
- 如果使用的大小写与关键字不一定则是可以使用
- 如果跟关键字一模一样则不被允许
变量命名规范
- 见名知意
- 尽量做到,看到名字,就知道是什么意思
- 确保在明了的前提下,减少名字的长度
- 下划线命名法
- 多个单词组合变量名,要使用下划线做分隔
- 英文字母全小写
- 命名变量中英文字母,尽量全小写
运算符
算术运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 | 两个对象相加 |
- | 减 | 得到负数,或是一个数减去另一个数 |
* | 乘 | 两个数相乘或是返回被重复诺干次的字符串a*b输出结果 |
/ | 除 | 两个数相除 |
// | 取整除 | 返回商的整数部分 |
% | 取余 | 返回除法的余数 |
** | 指数 | a**b为10的20次方 |
赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
= | 赋值运算符 | 把=号右边的结果赋值给=左边的变量 |
复合赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
+= | 加法赋值运算符 | c += a c = c + a |
-= | 减法赋值运算符 | c -= a c = c - a |
*= | 乘法赋值运算符 | c *= a c = c * a |
/= | 除法赋值运算符 | c /= a c = c / a |
%= | 取模赋值运算符 | c %= a c = c % a |
**= | 幂赋值运算符 | c **= a c = c ** a |
//= | 取整除赋值运算符 | c //= a c = c // a |
字符串扩展
- 字符串三种定义方式
- 单引号定义法 name = ‘xxxxx’
- 双引号定义法 name = “xxxxxx”
- 三引号定义法 name = “”“xxxxx”“”
- 三引号定义法和多行注释的写法一样,同样支持换行操作
- 使用变量接受它,他就是字符串
- 不使用变量接收它,就可以作为多行注释使用
- 字符串的引号嵌套
- 单引号定义法,可以内含双引号
- 双引号定义法可以内含单引号
- 可以使用转义字符(\)来将引号解除作用,变成普通字符串
- 字符串拼接
- 通过+ 号拼接
- **注意:**只能适用于字符串的拼接,不能使用字符串与非字符串的拼接
- 通过+ 号拼接
- 字符串格式化
num = 10
class_num = 10
message = "年段第%s班级,共有%s人" % (num, class_num)
"""
其中 %s表示将变量变成字符串放入占位的地方(这里将数字变成字符串放入)
% 表示: 我要占位
注意: 多个变量占位,变量要用括号括起来,并按照占位的顺序引入
python 支持多种数据类型占位
1. %s 转成字符串
2. %d 转成整数
3. %f 转成浮点数
"""
- 格式化的精度控制
- 我们可以使用辅助符号“m.n”来控制数据的宽度和精度
- m,控制宽度,要求数数字(很少使用),设置宽度小于数字自身,不生效
- .n, 控制小数点精度,要求是数字,会进行小数的四舍五入
实例:%5d: 表示将整数的宽度控制在5位,如数字11,被设置为5d,就会变成 [空格][空格][空格]11, 用三个空格补足宽度%5.2f, 表示将宽度控制在5,将小数点精度设置为2。小数点和小数部分也算入宽度计算,如对11.345设置了%7.2f后,结果是 :[空格][空格]11.35, 2个空格补足宽度,小数点部分限制2位精度,四舍五入为.35%.2f: 表示不限制宽度,只设置小数点精度为2,如11.345设置后为11.35
- 字符串格式化方式2(快速格式化)
- 通过语法 f"内容{变量}" 的格式来快速格式化 f"xxxx{name}xxxxx"
- 不限数据类型,不做精度控制
- 通过语法 f"内容{变量}" 的格式来快速格式化 f"xxxx{name}xxxxx"
- 对表达式进行格式化 – 一条具有明确执行结果的代码语句 1 + 1 5 * 2
- 用法: 将上面的变量转变成表达式
name = "xxxx"
stock_price = 11.11
stock_code = 100000
stock_price_daily_factor = 1.2
growth_days = 7print(f"公司{name}, 股票代码:{stock_code} 当前股价:{stock_price}")
print("每日增长系数:%.2f, 经过%d天, 股价变成了%.2f" % (stock_price_daily_factor, growth_days, stock_price * stock_price_daily_factor ** growth_days))
数据输入
input 语句(函数) 用来获取键盘输入数据输出:print数据输入: input
使用方法:使用input()语句可以从键盘上获取输入使用一个变量接受存储input语句获取的键盘输入数据即可代码:
print("输入名字:")
name = input() # 回车结束输入
print(name)name = input("输入名字:")
print(name)
判断语句
布尔类型和比较运算符
布尔类型
- 字面量:
- True 表示真
- Flase 表示假
- 定义变量存储布尔类型数据
- 变量名称 = 布尔类型字面量
布尔类型不仅可以自定定义,也可以通过计算的来,也就是使用比较运算符进行运算得到的布尔类型的结果
比较运算符
运算符 | 描述 | 示例 |
---|---|---|
== | 判断内容是否相等,满足True,不满足False | a = 3 b = 3 a == b为True |
!= | 判断内容是否不相等 | a = 1 b = 3 a != b True |
> | ||
< | ||
>= | ||
<= |
if语句的基本格式
基本语法格式
if 要判断的条件:条件成立,要做的事情例子代码:
if age >= 18:print("已经成年")判断语句的结果,必须是布尔类型True或者False
True会执行if内的代码语句
False则不会执行age = int(input("你的年纪:"))
if age >= 18:print("你已经成年,门票30")if age < 18:print("你未成年,门票10")
if else 语句
if 条件:满足 TODO
else:不满足 TODO例子:
age = int(input("你的年纪:"))
if age >= 18:print("你已经成年,门票30")
else:print("你未成年,门票10")
- if else 语句中
- if和其他代码块,条件满足时执行
- else搭配if的判断条件,当不满足时候执行
- if else 注意事项
- else 不需要判断条件,当if的条件不满足,else执行
- else代码块,同样需要4个空格作为缩进
if elif else 语句
if 条件1:满足条件1 TODO
elif 条件2:满足条件2 TODO
elif 条件3:满足条件3 TODO
else:都不满足 TODO例子:
age = int(input("你的年纪:"))
if age > 60:print("大于60岁免费")
elif age >= 18:print("你已经成年,门票30")
else:print("你未成年,门票10")
注意:判断是互斥且有顺序的
- 满足1 将不会执行2和3
- 满足2 将不会执行3
- 全不满足则进入else
- else可省略不写 , 效果等于几个独立的if判断
- 空格缩进同样不可省略
判断语句的嵌套
if 条件1:满足条件1 TODOif 条件2:满足条件2, TODO
第二个if 属于第一个if内,只有第一个if满足条件,才会执行第二个if
嵌套的关键点,在于:空格缩进
通过空格缩进,来决定语句之间的层次关系
- 嵌套判断语句可以使用于多条件,多层次的逻辑判断
- 嵌套判断语句可以根据需求,自由组合if elif else 来构建多层次判断
- 嵌套判断语句,一定要注意空格缩进,Python通过空格缩进来决定层次关系
实战案例
import randomnum = random.randint(1, 10) # 随机数 1到10以内的整型随机数
guess_num = int(input("输入你要猜测的数字"))if guess_num == num:print("恭喜你,猜中了")else:if guess_num > num:print("大了")else:print("小了")guess_num = int(input("输入你要猜测的数字"))if guess_num == num:print("恭喜你,猜中了")else:if guess_num > num:print("大了")else:print("小了")guess_num = int(input("输入你要猜测的数字"))if guess_num == num:print("恭喜你,猜中了")else:if guess_num > num:print(f"大了, 游戏结束,数字为:{num}")else:print(f"小了,游戏结束,数字为:{num}")
循环语句
while循环的基础语法
while 条件:条件满足 TODO....直到条件不满足则退出循环
### 注意: 只要条件一直满足,会无限循环执行,造成死循环例子:
i = 0
while i < 100:print(i)i += 1
# 循环输出0-99,直到i = 100 不满足i < 100 退出循环例子:求1-100的和
i = 1
num_sum = 0
while i <= 100:num_sum += ii += 1
print(num_sum)
注意:
- 条件需要提供布尔类型结果,True继续, False停止
- 空格缩进不能忘
- 规划好循环终止条件,否则将无限循环
while循环的基础案例
猜数字使用while改进:
import random
num = random.randint(1, 10) # 随机数 1到10以内的整型随机数
count = 1
flag = True
while flag:guess_num = int(input("输入你要猜测的数字"))if guess_num == num:print(f"恭喜你,猜中了用猜了{count}次")flag = Falseelse:if guess_num > num:print("大了,继续")else:print("小了, 继续")count += 1
while循环的嵌套应用
while 条件1:条件满足 TODO....while 条件2:满足条件2 TODO....直到条件2不满足则退出循环直到条件1不满足则退出循环
注意:
- 注意条件的控制,避免无限循环
- 多层嵌套,主要空格缩进来确定层次关系
- 循环条件的控制,层次越多越复杂,需要细心
while循环的嵌套案例
补充知识:
1. print输出不换行 在print语句中加入 end="" 即输出不换行
2. 制表符 \t 效果等于在键盘上按下tab键 可以让多行字符串进行对齐
案例: 九九乘法表
i = 1
while i <= 9:j = 1while j <= i:print(f"{j} * {i} = {i * j}\t", end="")j += 1i += 1print()
for 循环的基础语法
除while循环外。python支持for循环语句
区别:
- while循环的循环条件是自定义,自行控制循环条件
- for循环是一种“轮询”机制,是对一批内容进行“逐个处理”
语法:
for 临时变量 in 待处理数据集:
循环满足条件时执行的代码
从待处理数据集中,逐个取出数据赋值给临时变量
语法中:待处理数据集,称之为:序列类型
序列类型值:其内容可以一个个依次取出来的一种类型,包括:
- 字符串
- 列表
- 元组
- 等等
range语句:获得一个简单的数字序列
- 语法1:
- range(num):获取一个从0开始,到num结束的数字序列 (不包含num本身)
- 如range(5) 得到数据为[0,1,2,3,4]
- 语法2:
- range(num1. num2):获取一个从num1开始,到num2结束的数字序列(不包含num2本身)
- range(5, 10)获取的数据是:[5, 6, 7, 8, 9]
- 语法3:
- range(num1, num2, step):获取一个从num1开始,到num2结束的数字序列(不包含num2本身)
- 数字之间的步长,以step为准(step默认为1)
- range(5, 10, 2) 获取的数据为:[5, 7, 9]
变量作用域:
- for循环中的临时变量,其作用域限定为 循环内
- 这种限定:
- 是编程规范的限定,而非强制性限定
- 不遵守也能正常运行,但是不建议这么做
- 如需访问临时变量,可以预先在循环外定义它
语法
例子:遍历字符串
name = "ccyylllc"
for x in name:print(x)# for 循环将字符串的内容依次取出,for循环也被称为:遍历循环例子: 计算a出现的次数
count = 0
str = "xasasasagagsdga"
for s in str:if s == 'a':count += 1
print(count)例子: 1 - 100 (不包含100) 有几个偶数
count = 0
for i in range(1, 100):if i % 2 == 0:count += 1
print(count)
注意:
- 无法定义循环条件,只能被动取出数据处理
- 循环内的语句,需要有空格缩进
for循环的嵌套应用
语法:
for 临时变量1 in 待处理数据集:
满足循环条件的TODO
…
for 临时变量2 in 待处理数据集:
满足循环条件的TODO
注意事项:
- 注意缩进
- for循环可以和while循环相互嵌套使用
for i in range(1, 10):j = ifor j in range(1, i+1):print(f"{j} * {i} = {i * j}\t", end="")print()
循环中断: break和continue
continue:
- continue关键字用于中断本次循环,直接进入下一次的循环
- for和while循环,效果一直
break:
- break关键字同样只可以控制 它所在的循环结束
- for和while循环,效果一直
在嵌套循环中,只能作用在所在循环上,无法对上层循环起作用
综合案例
import randomall_price = 10000
person_num = 20
i = 1
while i <= 20:score = random.randint(1, 10)if score < 5:print(f"第{i}号员工,绩效{score}低于5分,不发工资")else:if all_price == 0:print("工资发完了,下个月再说")breakall_price -= 1000print(f"第{i}号员工,绩效{score},发工资1000,公司余额:{all_price}")i += 1
Python 函数
定义:
是组织好的,可重复使用,用来实现特定功能的代码段
为什么要使用函数:
- 为了得到一个针对特定需求,可供重复使用的代码段,提高程序的复用性,减少重复代码,提高开发效率
函数的定义
语法:
def 函数名(传入参数):
函数体
return 返回值
调用:
函数名(参数)
注意事项
- 参数如不需要,可以省略
- 返回值不需要,可以省略
- 函数必须先定义后使用
函数的参数
- 传入的参数的功能是:在函数进行计算的时候,接收外部(调用时)提供的数据
- 函数定义中,提供的参数称之为:形式参数,表示函数声明将要使用几个参数
- 参数之间用逗号隔开
- 函数调用中,提供的数据称之为:实例参数,标识函数执行时真正使用的参数值
- 传入的时候,按照顺序传入数据,使用逗号分隔
def check(x):if x >= 37.5:print("你已经发烧,需要隔离")else:print("体温正常")
check(37.7)
函数的返回值
定义:函数在执行完成后,返回给调用者的结果
返回值的应用语法:
- 使用关键字: return来返回结果
注意:
- 函数体在遇到return后就结束了,所以return后面的代码不会执行
None类型:
如果没有使用return语句返回数据,那么函数有返回值吗?
有的,Python中有一个特色的字面量,None,其类型是:<class ‘NoneType’>
无返回值的函数,实际上就是返回了None这个字面量
None表示 空的,无实际意义
函数返回的None 就标识,这个函数没有返回什么有意义的内容,也就是返回了空的意思
应用场景:
- 用在函数无返回值上
- 用在if判断上
- 在if判断中,None等同于False
- 一般用于在函数中主动返回None,配合if判断做相关处理
- 用于声明无内容的变量上
- 定义变量,但是暂时不需要有具体在,可以使用None来代替
函数说明文档
def check(x):"""返回判断结果:param x::return:"""if x >= 37.5:print("你已经发烧,需要隔离")else:print("体温正常")# 快捷注释:在函数名称下面输入6个引号后在中间回车
函数的嵌套调用
指:一个函数里面又调用了另外一个函数
执行流程:
- 函数A中执行到调用的函数B的语句,会将函数B全部执行完成后,继续执行函数A的剩余内容
变量的作用域
- 什么是局部变量
- 作用范围在函数内部,在函数外部无法使用
- 什么是全局变量
- 在函数内部和外部均可使用
- 如果将函数内定义的变量声明为全局变量
- 使用global关键字,global变量
num = 200
def change_num1():num = 100def change_num2():global numnum = 300change_num1()
print(num) # 结果 200 因为change_num1 赋值的num的局部变量
change_num2()
print(num) #结果 300 使用global声明变量为全局变量
综合案例
money = 20000
name = "xxxx"def init():print("-----------主菜单-----------")print("查询余额 1")print("存款 2")print("取款 3")print("退出 4")return int(input("请输入你的选择:"))def search():print("----------查询余额-----------")print(f"{name}的剩余金额:{money}")def getOrSet(use_money, flag):global moneyif flag == 1:money += use_moneyelse:money -= use_moneyprint(f"你的{'存款' if flag == 1 else '取款'}操作成功,现在余额{money}")def close():print("退出成功")if __name__ == '__main__':while True:ops = init()if ops == 1:search()elif ops == 2:getOrSet(200, 1)elif ops == 3:getOrSet(400, 2)elif ops == 4:close()breakelse:print("输入错误,请重新输入")continue
数据容器
定义:
一种可以容纳多份数据的数据类型,容纳的每一份数据称之为1个元素,每一个元素,可以是任意类型的数据,如字符串,数字,布尔等
特点:
- 是否支持重复元素
- 是否可以修改
- 是否有序
- 等等
分为5类:
- 列表(list)
- 元组(tuple)
- 字符串(str)
- 集合(set)
- 字典(dict)
list 列表
列表的定义
# 字面量
[元素1, 元素2, 元素3, 元素4, ...]
# 定义元素
变量名称 = [元素1, 元素2, 元素3, 元素4, ...]
# 定义空列表
变量名称 = []
变量名称 = list()注意:列表可以一次存储多个数据,且可以为不同的数据类型,支持嵌套
列表的下标索引
- 下标的索引是什么:列表的每一个元素,都有编号成称之为下标索引
- 从前往后的方向,编号从0开始递增
- 从后往前的方向,编号从-1开始递减
- 如何通过下标索引取出对应的元素
- 列表[下标],即可取出
- 下标索引的注意事项:
- 要注意下标索引的取值范围,超出范围无法取出元素,并且会报错
列表的常用操作
列表的查询功能(方法)
- **查找某元素的下标 **
- 功能:查找指定元素在列表的下标,如果没找到,则报错ValueError
- 语法 列表.index(元素) :index就是列表元素对象(变量)内置的方法(函数)
- 统计列表内,有多少元素
- 语法 len(列表)
列表的修改功能(方法)
- 修改特定位置(索引)的元素值:语法: 列表[下标] = 值
- 可以使用如上写法,直接对指定下标(正向,反向都可以)的值进行:重新复制(修改)
- 插入元素
- 语法: 列表.insert(下标,元素) 在指定的下标位置,插入指定的元素
- 追加元素:
- 语法:列表.append(元素) 将指定元素,追加到列表的尾部
- 追加元素2
- 语法:列表.extend(其他数据容器), 将其他数据容器的内容取出,依次追加到列表尾部
- 删除元素
- 语法1 del 列表[下标]
- 语法2 列表.pop(下标)
- 删除某元素在列表中的第一匹配项
- 语法: 列表.remove(元素)
- 清空列表内容
- 语法 列表.clear()
- 统计某元素在列表中的数量
- 语法:列表.count(元素)
特点:
- 可以容纳多个元素(上限 2**63-1)
- 可以容纳不同类型的元素(混装)
- 数据是有序存储的(有下标序号)
- 允许重复数据存在
- 可以修改(增加删除元素等等)
my_list = ["demo1", "demo2", "demo3", "demo4"]
print(my_list.index("demo1")) # 0
print(my_list.index("demo0")) # ValueError: 'demo0' is not in listmy_list[0] = "demo0"
my_list[-1] = "demo-1"
my_list[4] = "demo4" # IndexError: list assignment index out of range
print(my_list[0]) # demo0
print(my_list[-1]) # demo-1
print(my_list[4])list_len = len(my_list)
print(list_len) # 4my_list.insert(3, "demo5")
print(my_list) # ['demo1', 'demo2', 'demo3', 'demo5', 'demo4']my_list.append("demo6")
print(my_list) # ['demo1', 'demo2', 'demo3', 'demo5', 'demo4', 'demo6']my_list.extend(["demo7", "demo8", "demo10"])
print(my_list) # ['demo1', 'demo2', 'demo3', 'demo5', 'demo4', 'demo6', 'demo7', 'demo8', 'demo10']del my_list[8]
print(my_list) # ['demo1', 'demo2', 'demo3', 'demo5', 'demo4', 'demo6', 'demo7', 'demo8']
del_str = my_list.pop(7)
print(my_list, del_str) # ['demo1', 'demo2', 'demo3', 'demo5', 'demo4', 'demo6', 'demo7'] demo8my_list.remove("demo2")
print(my_list) # ['demo1', 'demo3', 'demo5', 'demo4', 'demo6', 'demo7']num = my_list.count("demo1")
print(num) # 1my_list.clear()
print(my_list) # []"""
有一个列表,内容是[21, 25, 21, 23, 22, 20], 记录一批学生年纪
"""# 定义一个列表,并用变量接受它
my_list = [21, 25, 21, 23, 22, 20]
# 追加一个数字31到列表微博
my_list.append(31)
print(my_list) # [21, 25, 21, 23, 22, 20, 31]
# 追加一个新列表[29, 33, 30] 到尾部
my_list.extend([29, 33, 30])
print(my_list) # [21, 25, 21, 23, 22, 20, 31, 29, 33, 30]
# 取出第一个元素 [21]
num1 = my_list.pop(0)
print(num1) # 21
# 取出最后一个元素
num2 = my_list.pop(len(my_list) - 1)
print(num2) # 30
# 查找元素31,在列表中下标位置
print(my_list.index(31)) # 5
list的遍历
使用while
index = 0
while index < len(list):
print(list[index])
index += 1
使用for循环
for 临时变量 in 数据容器
对临时变量进行处理
for element in list:
print(element)
while循环和for循环,都是循环语句,但是细节不同
- 在循环上控制
- while循环可以自定循环条件。并自行控制
- for循环不可以自定循环条件,只可以一个个从容器取出数据
- 在无限循环上:
- while循环可以通过条件控制做到无限循环
- for循环理论上不可以,因为被遍历的容器容量不是无限的
- 在使用场景上
- while循环适用于任何想要循环的场景
- for循环适用于,遍历数据容器的场景或者简单的固定次数循环场景
数据容器:tuple(元组)
为什么需要元组:如果想要传递的信息,不被篡改,列表就不合适
元组用列表一样,都是可以封装多个,不同类型的元素在内。
但是最大的不同点在于:
元组一旦定义完成,就不可修改
所以当我们需要在程序内封装数据,又不希望封装的数据被篡改,那么元组就非常合适
定义元组:
定义元组使用小括号,且使用逗号合开各个数据,数据可以是不同的数据类型
# 定义元组字面量
(元素, 元素, .... , 元素)
# 定义元组变量
变量名称 = (元素, 元素, .... , 元素)
# 定义空元组
变量名称 = ()
变量名称 = tuple() 注意:元组只有一个数据,这个数据后面要添加逗号t1 = ((1, 2, 3), (4, 5, 6))# 通过下标索引取出数据 6
num = t1[1][2]
元组的相关操作:
- index(元素) 查找某个数据,如果数据存在返回对应的下标,否则报错
- count(元素) 统计某个数据在当前元组出现的次数
- len(元组) 统计元组内的元素个数
# 元组遍历
# while遍历
index = 0
while index < len(元组):print(元组[index])index += 1
# for遍历
for item in 元组:print(item)
元组特点:
- 可以容纳多个数据
- 可以容纳不同类型的数据
- 数据是有序存储 – 下标索引
- 允许重复数据存在
- 不可以修改(整加或者删除)
- 支持for循环
元组注意事项:
- 不可以修改元组的内容,否则会直接报错
- 可以修改元组内的list(修改元素,增加,删除,反转等等)
数据容器:str(字符串)
字符串是字符的容器,一个字符串可以存放任意数量的字符
字符串的下标(索引)
和其他容器一样,列表,元组一样,字符串也可以通过下标来进行访问
- 从前向后,下标为0开始
- 从后向前,下标为-1开始
同元组一样,字符串是一个:无法修改的数据容器
如果必须要修改,只能得到新的字符串,老字符串无法修改
字符串常用的操作
- 字符串的替换
- 语法:字符串.replace(字符串1, 字符串2)
- 功能:将字符串内的全部内容:字符串1 替换成字符串2
- 注意:不是修改字符串本身。而是得到一个新的字符串
- 字符串分割
- 语法: 字符串.split(分割符字符串)
- 功能:按照指定的分隔符字符串,将字符串划分为多个字符串,并存入列表对象中
- 注意:字符串本身不变,而是得到一个列表对象
- 字符串的规整操作(去前后空格)
- 语法:字符串.strip()
- 字符串的规整操作 (去前后指定字符串)
- 语法:字符串.strip(字符串)
- 统计字符串某个字符串出现的次数
- 语法: count(字符串)
- 统计字符串长度
- 语法:len(字符串)
my_str = " iitemasdadfad badfa "
my_str2 = "12iitemasdadfad badfa21"
# index 方法
print(my_str.index("ad")) # 8# replace方法
print(my_str.replace("ad", ",,")) # iitemasd,,f,, b,,fa# split方法
print(my_str.split("a")) # ['iitem', 'sd', 'df', 'd b', 'df', ' ']
print(my_str.split("a", 2)) # ['iitem', 'sd', 'dfad badfa ']# strip方法
print(my_str.strip()) # iitemasdadfad badfa
print(my_str2.strip("12")) # iitemasdadfad badfa
# 注意:传入的是12,其实是"1"和"2" 都会被移除,是按照单个字符# 统计字符串中某字符串出现的次数
print(my_str.count("ab")) # 0
# 统计字符串的长度
print(len(my_str)) # 21
数据容器的切片
序列是指:内容连续,有序,可使用下标索引的一类数据容器
列表,元组,字符串,均可以视为序列
序列支持切片,即列表,元组,字符串支持进行切片操作,
切片:从一个序列中,取出一个子序列
语法:序列[起始下标: 结束下标: 步长]
表示从序列中,从指定位置开始,依次取出元素,到指定位置结束,得到一个新序列
- 起始下标表示从何处开始,可以留空,留空则表示从头开始
- 结束下标(不含)表示何处结束,可以留空,留空视为截取到结果
- 步长表示,依次取出元素的间隔
- 步长1表示,一个个取元素
- 步长2表示,每次跳过一个取元素
- 步长N表示,每次跳过N-1个元素取
- 步长为负数表示,反向取,(注意:起始下标和结束下标也要反向标记)
- 注意:此操作不会影响序列本身,而是会得到一个新的序列
# 对list进行切片,从1开始,4结束,步长1
my_list = [0, 1, 2, 3, 4, 5, 6]
print(my_list[1:4]) # 步长1可以默认不写 [1, 2, 3]# 对tuple进行切片,从头开始,到最后结束,步长1
my_tuple = (0, 1, 2, 3, 4, 5, 6)
print(my_tuple[:]) # 起始和结束不写表示从头到尾,步长为1省略 (0, 1, 2, 3, 4, 5, 6)# 对str进行切片,从头开始,到最后结束,步长2
my_str = "01234567"
print(my_str[::2]) # 0246# 对str进行切片,从头开始,到最后结束,步长-1
my_str2 = "01234567"
print(my_str2[::-1]) # 等同于将序列反转 76543210# 对列表进行切片,从3开始,到1 结束,步长-1
my_list2 = [0, 1, 2, 3, 4, 5, 6]
print(my_list2[3:1:-1]) # [3, 2]# 对元组进行切片,从头开始,到尾结束,步长-2
my_tuple2 = (0, 1, 2, 3, 4, 5, 6)
print(my_tuple2[::-2]) # (6, 4, 2, 0)
数据容器:set(集合)
语法:
# 定义集合字面量
{元素, 元素, 元素, ..., 元素}
# 定义集合变量
变量名称 = {元素, 元素, 元素, ..., 元素}
# 定义空集合
变量名称 = set()
集合的操作:(因为集合是无序的,所以集合不支持:下标索引)但是和列表一样是允许修改的
- 添加新元素
- 语法:集合.add(元素),将指定元素,添加到集合内
- 结果:集合本身被修好,移除了新元素
- 移除元素
- 语法:集合.remove(元素),将指定元素,从集合内移除
- 结果:集合本身被修好,移除了新元素
- 从集合中随机取出元素
- 语法:集合.pop()功能,从集合中随机取出一个元素
- 结果:会得到一个元素的结果,同时集合本身被修改,元素被移除
- 清空集合
- 语法:集合.clear() 功能,清空集合
- 结果:集合本身被清空
- 取出两个集合的差集
- 语法:集合1.difference(集合2),
- 功能:取出集合1和集合2的差集,(集合1有而集合2没有的)
- 结果:得到一个新集合,集合1和集合2不变
- 消除两个集合的差集
- 语法:集合1.difference_update(集合2)
- 功能:对比集合1和集合2,在集合1内,删除和集合2相同的元素
- 结果:集合1被修改,集合2不变
- 两个集合合并
- 语法:集合1.union(集合2)
- 功能:将集合1和集合2组成新集合
- 结果:得到新集合,集合1和集合2不变
my_set = {"demo1", "demo2", "demo3", "demo4"}# 添加新元素
my_set.add("demo5")
print(my_set) # {'demo3', 'demo4', 'demo1', 'demo5', 'demo2'} 输出没按照顺序,因为他是无序的# 移除新元素
my_set.remove("demo5")
print(my_set) # {'demo3', 'demo4', 'demo1', 'demo2'}# 随机去除一个元素
str = my_set.pop()
print(str) # demo3# 清空集合
my_set.clear()
print(my_set) # set()# 取出两个集合的差集
set1 = {1, 2, 3, 4}
set2 = {3, 4, 7, 8}
set3 = set1.difference(set2)
print(set1) # {1, 2, 3, 4}
print(set2) # {8, 3, 4, 7}
print(set3) # {1, 2}# 消除两个集合的差集
set1 = {1, 2, 3, 4}
set2 = {3, 4, 7, 8}
set3 = set1.difference_update(set2)
print(set1) # {1, 2}
print(set2) # {{8, 3, 4, 7}
print(set3) # None# 2个集合合并为1个
set1 = {1, 2, 3, 4}
set2 = {3, 4, 7, 8}
set3 = set1.union(set2)
print(set1) # {1, 2, 3, 4}
print(set2) # {8, 3, 4, 7}
print(set3) # {1, 2, 3, 4, 7, 8}# 统计集合元素的数量
set1 = {1, 2, 3, 4}
print(len(set1)) # 4# 集合遍历
set1 = {1, 2, 3, 4}
for item in set1:print(item)
特点:
- 可以容纳多个数据
- 可以容纳不同类型的数据
- 数据是无序存储的(不支持下标索引)
- 不允许重复数据存在
- 可以修改(增删改查)
- 支持for循环
数据容器:dict(字典,映射)
定义:同样使用{},不过存储的元素是一个个的:
# 定义字典字面量
{key: value, key: value, key: value, ..., key: value}
# 定义字典变量
my_dict = {key: value, key: value, key: value, ..., key: value}
# 定义空字典
my_dict = {}
my_dict = dict()
数据的获取:
- 字典可以通过key值去取得对应的value
注意事项:
- 键值对的key和value可以是任意类型(key不能为字典)
- 字典内的key不允许重复,重复添加等同于覆盖原有数据
- 字典不支持下标索引,而是用过key检索value
字典常用超出:
- 新增元素:
- 语法:字典[key] = value,
- 结果:字典被修改,新增了元素
- 更新元素:
- 语法:字典[key] = value
- 结果:字典被修改,元素被更新
- 注意:字典key不可以重复,所以对已存在的key执行上述操作,就是更新value值
- 删除元素:
- 语法:字典.pop(key)
- 结果:获取指定key的value
- 清空字典
- 语法:字典.clear()
- 结果:字典被修改,元素被清空
- 获取全部的key
- 语法:字典.keys()
- 结果:得到字典中全部的key
my_dict = {"demo1": 1, "demo2": 2, "demo3": 3}# 新增元素
my_dict["demo4"] = 4
print(my_dict) # {'demo1': 1, 'demo2': 2, 'demo3': 3, 'demo4': 4}# 更新元素
my_dict["demo3"] = 33
print(my_dict) # {'demo1': 1, 'demo2': 2, 'demo3': 33, 'demo4': 4}# 删除元素
my_dict.pop("demo3")
print(my_dict) # {'demo1': 1, 'demo2': 2, 'demo4': 4}# 清空元素
my_dict.clear()
print(my_dict) # {}# 获取全部的key
my_dict = {"demo1": 1, "demo2": 2, "demo3": 3}
print(my_dict.keys()) # dict_keys(['demo1', 'demo2', 'demo3'])
# 遍历字典
for item in my_dict.keys():print(item, end="") # dict_keys(['demo1', 'demo2', 'demo3'])print(my_dict[item], end="") # demo11demo22demo33# 统计字典内的元素数量
my_dict = {"demo1": 1, "demo2": 2, "demo3": 3}
print(len(my_dict)) # 3
数据容器总结对比
- 是否支付下标索引
- 支持:列表,元组,字符串 – 序列类型
- 不支持:集合,字典 – 非序列类型
- 是否支持重复元素
- 支持:列表,元组,字符串 – 序列类型
- 不支持:集合,字典 – 非序列类型
- 是否可以修改
- 支持: 列表,集合,字典
- 不支持:元组,字符串
数据容器的通用操作
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_Str = "adbadfgh"
my_dict = {"key1": 1, "key2": 2, "key3": 3}
my_set = {1, 2, 3, 4, 5}# len 元素个数
print(len(my_list)) # 5
print(len(my_tuple)) # 5
print(len(my_Str)) # 8
print(len(my_dict)) # 3
print(len(my_set)) # 5# max最大元素
print(max(my_list)) # 5
print(max(my_tuple)) # 5
print(max(my_Str)) # h
print(max(my_dict)) # key3
print(max(my_set)) # 5# min最小元素
print(min(my_list)) # 1
print(min(my_tuple)) # 1
print(min(my_Str)) # a
print(min(my_dict)) # key1
print(min(my_set)) # 1# 类型转换: 容器转列表
print(list(my_list)) # [1, 2, 3, 4, 5]
print(list(my_Str)) # ['a', 'd', 'b', 'a', 'd', 'f', 'g', 'h']
print(list(my_tuple)) # [1, 2, 3, 4, 5]
print(list(my_set)) # [1, 2, 3, 4, 5]
print(list(my_dict)) # ['key1', 'key2', 'key3']# 容器转元组
print(tuple(my_list)) # (1, 2, 3, 4, 5)
print(tuple(my_Str)) # ('a', 'd', 'b', 'a', 'd', 'f', 'g', 'h')
print(tuple(my_tuple)) # (1, 2, 3, 4, 5)
print(tuple(my_set)) # (1, 2, 3, 4, 5)
print(tuple(my_dict)) # ('key1', 'key2', 'key3')# 容器转集合
print(set(my_list)) # {1, 2, 3, 4, 5}
print(set(my_Str)) # {'g', 'd', 'f', 'h', 'b', 'a'}
print(set(my_tuple)) # {1, 2, 3, 4, 5}
print(set(my_set)) # {1, 2, 3, 4, 5}
print(set(my_dict)) # {'key1', 'key2', 'key3'}# 容器转字符串
print(str(my_list)) # [1, 2, 3, 4, 5]
print(str(my_Str)) # adbadfgh
print(str(my_tuple)) # (1, 2, 3, 4, 5)
print(str(my_set)) # {1, 2, 3, 4, 5}
print(str(my_dict)) # {'key1': 1, 'key2': 2, 'key3': 3}
综合案例
函数的多返回值
示例:
def test_return():
return 1, 2
x, y = test_return
按照返回值的顺序,写对应顺序的多个变量接收即可,变量之间用逗号隔开,支持不同类型的数据return
函数的多种传参方法
使用方式上的不同,函数有4中常见参数使用方式
- 位置参数
def user_info(name, age, gender):TODO
user_info("name", "age", "gender")
# 注意: 传递的参数和定义的参数的顺序及个数必须一致
- 关键字参数
# 关键字参数:函数调用时通过“键=值”形式传递参数
# 作用:可以让函数更加清晰,容易使用,同时也消除了参数的顺序
def user_info(name, age, gender)TODO
# 关键字传参
user_info(name = "name", age = "age", gender = "gender")
# 可以不按照固定顺序
user_info(age = "age", gender = "gender", name = "name")
# 可以和位置参数混用,位置参数必须在前面,且匹配参数顺序
user_info("name", age = "age", gender = "gender")
# 注意:函数调用时,如果有位置参数时,位置参数必须在关键字参数前面,但关键字参数之间不存在先后顺序
- 缺省参数
"""
缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数是可不传该默认参数的值(注意: 所有位置参数必须出现在默认参数前,包括函数的定义和调用)
作用: 当调用函数时没有传递参数,就会使用默认是用缺省参数对应的值
"""
def user_info(name, age, gender = "男")TODOuser_info("name", "age")
user_info("name", "age", "gender")注意:
函数调用时,如果为缺省参数传递则修改默认值,否则使用这个默认值
- 不定长参数
"""
不定长参数也叫可变参数,用于不确定调用的时候,会传递多少个参数(不传参也可以)的场景
作用: 当调用函数时不确定参数的个数时,可以使用不定长参数不定长参数的类型:
1.位置传递
2.关键字传递
"""
# 位置传递
def user_info(*args):TODO
user_info("name")
user_info("name","age")注意:传进的所有参数都会被args变量收集,它会根据传进参数的位置合并成一个元组,args是元组类型,这就是位置传递# 关键字传递
def user_info(**kwargs):TODO
user_info(name = "name", age = "age")# 注意:参数是“键值对”形式的情况下,都会被keyWords接收,同时会根据"键值对"组成字典
函数作为参数传递
def test_func(compute):result = compute(1, 2)TODO
def compute(x, y):TODOtest_func(compute)
"""
函数compute,作为参数,传入了test_func函数中使用
test_func需要一个函数作为参数传入,这个函数需要接受两个数字进行计算,计算逻辑由这个被传入函数决定
compute函数接受2个数字对其进行计算,compute函数作为参数,传递给了test_func函数使用
最终,在test_func函数内部,由传入的compute函数,完成对数字的计算操作
这是一种计算逻辑的传递,而非数据的传递
任何逻辑都可以自定定义并作为函数传入
"""
lambda匿名函数:
函数定义中:
- def关键字,可以定义带有名称的函数
- lambda关键字,可以定义匿名函数(无名称)
有名称的函数,可以基于名称重复使用
无名称的匿名函数,只可临时使用一次
匿名函数定义语法:
lambda 传入参数: 函数体(一行代码)
- lambda是关键字 表示定义匿名函数
- 传入参数表示匿名函数的形式参数,如:x, y表示接受2个形式参数
- 函数体:就是函数的执行逻辑,要注意,只能写一行,无法写多行代码
def test_func(compute):result = compute(1, 2)print(result)test_func(lambda x, y: x + y)
文件操作
文件编码
- 什么是编码:
- 编码就是一种规则集合,记录了内容和二进制进行相互转换的逻辑
- 编码有许多种,我们最常见的是UTF-8编码
- 为什么要使用编码
- 计算机只认识0和1,所以需要将内容翻译成0和1才能保存在计算机中
- 同样也需要编码,将计算机保存的0和1反向翻译回可以识别的内容
文件读取
- open()打开函数
在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新的文件
open(name, mode, encoding)
name: 要打开的目标文件名的字符串,可以包含文件所在的具体路径
mode: 设置打开文件的模式(访问模式):只读,写入, 追加等等
encoding:编码格式实例代码:
f = open("text.txt","r",encoding="utf-8")
注意:encoding的顺序不是第三位,所以不能用位置参数,用关键字参数直接指定、注意事项:此时的‘f’是‘open’函数的文件对象,
############################################################
mode 常用的三种基础访问模式
r: 以只读方式打开文件,文件的指针将会放在文件的开头,这是默认方式
w: 打开一个文件只用于写入,如果该文件已存在则打开文件,并从开头开始编辑,原有内容会被删除,如果该文件不存在,创建新文件
a: 打开一个文件用于追加,如果该文件写存在,新的内容将会写入到已有的内容之后,如果该文件不存在,创建新文件进行写入
读取相关操作
- read() 方法
- 语法: 文件对象.read(num)
- num表示要从文件中读取的数据的长度(单位字节)如果没有传入num,那么表示读取文件中所有的数据
- readlines()方法
- readlines()可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素
- readline()方法 一次读取一行内容
- 语法:文件对象.readline()
- for循环读取文件行
- for line in open(“文件位置”, “r”)
- print(line)
- 每一个line临时变量,就记录了文件的一行数据
- for line in open(“文件位置”, “r”)
- close() 关闭文件对象
- 最后通过close,关闭文件对象,也就是关闭对文件的占用,
- 最用不调用close,同时程序没有停止运行,那么这个文件将一直被python程序占用
- with open(“文件”, “r”) as f:
- f.readlines()
- 通过with open()的语句块中对文件进行操作
- 可以在操作完成后自动关闭close文件,避免遗忘掉close方法
"""
在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新的文件
open(name, mode, encoding)
name: 要打开的目标文件名的字符串,可以包含文件所在的具体路径
mode: 设置打开文件的模式(访问模式):只读,写入, 追加等等
encoding:编码格式实例代码:
f = open("text.txt","r",encoding="utf-8")
注意:encoding的顺序不是第三位,所以不能用位置参数,用关键字参数直接指定、注意事项:此时的‘f’是‘open’函数的文件对象,
"""# 打开文件
f = open("D:/Desktop/test.txt", "r", encoding="utf-8")
print(type(f)) # <class '_io.TextIOWrapper'>
# 读取文件 --- read()
print("f 读取10个字节的结果", f.read(10)) # f 读取10个字节的结果 1111111111
print("f 读取全部的结果", f.read()) # 文件全部数据
f.close()
# 读取文件 readlines()
f = open("D:/Desktop/test.txt", "r", encoding="utf-8")
lines = f.readlines()
print(type(lines)) # <class 'list'>
print(lines) # ['11111111111111111111111\n', '22222222222222222222222\n', '333333333333333333333333\n',
# '44444444444444444444444444\n', '55555555555555555555555555\n', '6666666666666666666666666666\n',
# '77777777777777777777777777777\n', '8888888888888888888888888888888\n', '9999999999999999999999999999999999\n',
# '101010101010101010101010101010101010\n', '11111111111111111111111111111111111111111']
f.close()
# 读取文件 readline() 一次读取一行
f = open("D:/Desktop/test.txt", "r", encoding="utf-8")
line = f.readline()
print(type(line)) # <class 'str'>
print(line) # 11111111111111111111111
f.close()count = 0
with open("D:/Desktop/test.txt", "r", encoding="utf-8") as f:for item in f.readlines():count += item.count("111")print(count) # 20
文件写入
写入快速入门:
案例演示:
# 打开文件
f = open("py.txt", "w")
# 文件写入
f.write("hello, word")
# 内容刷新
f.flush()## 注意:
1. 直接调用write,内容并为真正写入文件,而是会积攒在程序的内存里,称之为缓冲区
2. 当调用flush的时候,内容会真正写入文件
3. 这样做的避免频繁的操作硬盘,导致效率下降(赞一堆,一次性写磁盘)
写入文件使用open函数“w”模式进行写入
写入的方法有:
- write() 写入内容,
- flush(), 刷新内容到硬盘中
注意实现:
- w模式,文件不存在,会创建新文件
- w模式,文件不存在,会清空原有内容
- close()方法,带有flush()方法的功能
文件追加
案例演示:
# 1 打开文件,通过a模式打开即可
f = open("文件", "a")# 2. 文件写入
f.write("hello world)#3. 内容刷新
f.flush()## 注意
1. a模式下,文件不存在会创建文件
2. a模式下,文件存在会在最后追加写入文件
文件操作综合案例
with open("bill.txt", "r", encoding="utf-8") as fr:with open("bill.txt.bak", "w", encoding="utf-8") as fw:for line in fr:line = line.strip()if line.strip(",")[4] == "测试":continuefw.write(line)fw.write("\n")
python 异常、模块、包
什么是异常:
当我们检测到一个错误时,Python解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”我们常说的bug
异常的捕获方法
基本语法
try:可能发生错误的代码
except:如果出现异常执行的代码
快速入门
# 需求,尝试以'r'模式打开文件,如果文件不存在,则以'w'方式打开
try:f = open('linux.txt','r')
except:f = open('linux.txt','w')
捕获指定异常
基本语法:
try:print(name)
except NameError as e:print("name未定义")## 注意事项:
1. 如果尝试执行的代码得异常和要捕获的异常类型不一致,则无法捕获异常
2. 一般try下方只放一行尝试执行的代码
捕获多个异常
基本语法
## 当捕获多个异常时,可以把要捕获的异常类型的名字,放在except后,并用元组的方式进行书写
try:print(1/0)
except (NameError, ZeroDivisionError) as e:print("异常");# 捕获所有的异常
try:print(1/0)
except Exception as e:print("异常");
异常else
else表示如果没有异常要执行的代码----可写可不写
try:print(1/0)
except Exception as e:print("异常");
else:print("没有异常,执行的代码");
异常的finally
finally表示无聊是否异常都要执行的代码。例如关闭文件
try:print(1/0)
except Exception as e:print("异常");
else:print("没有异常,执行的代码");
finally:print("用了,一定会走")
异常的传递
异常是具有传递性的
当函数func01中发生异常,并且没有捕获处理这个异常的时候,异常会传递到函数fun02,当fun02也没有捕获处理这个异常的时候,main函数会捕获这个异常,这就是异常的传递性,
提示:
- 当所有函数都没有捕获异常的时候,程序就会报错
# 异常捕获
def fun01():print("这是func01开始")num = 1 / 0print("这是func01结束")def fun02():print("这是func02开始")fun01()print("这是func02结束")if __name__ == '__main__':try:fun02()except Exception as e:print(e)
############
这是func02开始
这是func01开始
division by zero# 异常没有捕获
def fun01():print("这是func01开始")num = 1 / 0print("这是func01结束")def fun02():print("这是func02开始")fun01()print("这是func02结束")if __name__ == '__main__':fun02()
################
这是func02开始
这是func01开始
Traceback (most recent call last):File "D:\Desktop\pySpace\py-demo\exceptiondemo.py", line 14, in <module>fun02()File "D:\Desktop\pySpace\py-demo\exceptiondemo.py", line 9, in fun02fun01()File "D:\Desktop\pySpace\py-demo\exceptiondemo.py", line 3, in fun01num = 1 / 0~~^~~
ZeroDivisionError: division by zero
Python模块
什么是模块
python模块(module)是一个python文件,以.py结尾,模块能定义函数,类和变量。模块里也能包含可执行的代码。
模块的作用
python中有很多各种不同的模块,每一个模块都可以帮助我们快速的实现一些功能,例如实现和时间相关的功能可以使用time模块,我们可以认为一个模块就是第一个工具包,每一个工具包都有各种不同的工具供我们使用,进而实现各种不同的功能,
模块导入的方式
模块在使用前需要先导入,导入的语法如下:
[from 模块名] import [模块 | 类 | 变量 | 函数 | *] [as 别名]
常用的组合:
- import 模块名
- from 模块名 import 类、变量、方法等
- from 模块名 import *
- import 模块名 as 别名
- from 模块名 import 功能名 as 别名
# 使用import 导入time模块使用sleep功能
import timetime.sleep(2000)# 使用from 导入time的sleep
from time import sleep
sleep(2000)# 使用* 导入time模块的全部功能
from time import *
sleep(2000)# 使用as给特定功能加上别名
from time import sleep as s
s(3000)
自定义模块的导入:
##### my_module
def test(a, b):print(a + b)
##### my_module2
def test(a, b):print(a - b)
#### my_module3
__add__ = ["test2", "test3"]def test(a, b):print(a - b)def test2(a, b):print(a * b)def test3(a, b):print(a / b)# 导入自定义模块使用
import my_modulemy_module.test(1, 2)# 导入不同模块的同名功能
from my_module import test
from my_module2 import test
## 注意: 当导入多个模块的时候,且模块内有同名功能,当调用这个同名功能的时候,调用到的是后面导入的模块的功能
test(2, 1)# __main__变量
只有当程序是直接执行的才会进入 if内部,如果是被导入的,则if无法进入# __all__变量
# 如果一个模块文件中有‘__all__’变量,当使用‘from xxx import *’导入时,只能导入这个列表中的元素
python包
什么是python包
- 从物理上看:包就是一个文件夹,在该文件夹下包好了一个_init_.py 文件,该文件夹可用于包含多个模块文件
- 从逻辑上看:包的本质还是模块
- 包的作用:当我们的模块文件越来越多时,包可以帮助我们管理这些模块,包的作用就是包含多个模块,但是包的本质还是模块
导入包
- 方式一
import 包名.模块名 | 包名.模块名.目标
- 方式二
from 包名 import * | 模块名.目标
## 注意:必须在'__init__.py'文件里面添加'__add__ = []' 控制允许导入的模块列表
第三方包
在python中有许多第三方包(非官方)
- 科学计算中常用的:numpy包
- 数据分析常用的:pandas包
- 大数据计算中常用的:pyspark,apache-flink包
- 图形可视化常用的:matplotlib,pyecharts
- 人工智能常用的:tensorflow
但是由于是第三方包,python没有内置,所以我们需要安装才可以导入使用
pip
第三方包安装非常简单,只需要使用python内置的pip程序即可
- pip install
- 如 pip install numpy
综合案例
my_utils包# __init__.py
__all__ = ["str_util", "file_util"]# file_util.py
def print_file_info(file_name):f = open(file_name, "r", encoding="utf-8")try:context = f.read()print(context)except Exception:print("文件不存在")finally:f.close()def add_file_data(file_name, data):with open(file_name, "a", encoding="utf-8") as f:f.write(data)f.close()# str_util.py
def str_reverser(s):return reversed(s)def sub_str(s, x, y):return s[x:y]# mian中使用
from my_utils import str_util, file_utilif __name__ == '__main__':print(str_util.str_reverser("asdfghj"))print(str_util.sub_str("asdfghhh", 2, 6))file_util.add_file_data("pydemo.txt", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""\nxxxxxxxxxxxxxxxxxxxxxxxxxx")file_util.print_file_info("pydemo.txt")
Python JSON数据格式的转换
什么是json:
- json是一种轻量级的数据交互格式,可以按照json指定的格式去组织和数据封装
- json本质上是一个特定格式的字符串
主要功能:json就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互,类似与
- 国际通用语言–英语
- 中国民族不同地区的通用语言—普通话
python数据和JSON数据的相互转化
# 导入json模块
import json# 准备符合格式json格式要求的python数据
data = [{"name":"老王", "age": 18},{"name":"老名", "age": 16}]# 通过json.dumps(data)方法把python数据转成json数据
data = json.dumps(data)
print(data) # [{"name": "\u8001\u738b", "age": 18}, {"name": "\u8001\u540d", "age": 16}]
# 加入ensure_ascii=False [{"name": "老王", "age": 18}, {"name": "老名", "age": 16}]# 通过json.loads(data)方法把json数据转化为python数据
data = json.loads(data)
print(data) # [{'name': '老王', 'age': 18}, {'name': '老名', 'age': 16}]
python面向对象
初识对象
在程序中是可以做到和生活中那样,设计表格,生产表格,填写表格的组织形式的
- 在程序中设计表格,称之为设计类 class
- 在程序中打印生产表格,称之为创建对象
- 在程序中填写表格,称之为对象属性复制
# 设计一个类
class Student:name = Noneage = Nonegender = None
# 创建一个对象
stu_1 = Student()
# 对象属性进行赋值
stu_1.name = "张三"
stu_1.age = 18
stu_1.gender = "女"
# 获取对象中记录的信息
print(stu_1.name, stu_1.age, stu_1.gender) # 张三 18 女
类的基本语法
class 类名称:
类的属性
类的行为
- class是关键字,表示要定义类了
- 类的属性,即定义在类中的变量
- 类的行为,即定义在类中的函数(成员方法)
创建类对象的语法
对象 = 类名称()
成员方法
什么是类的行为(方法)?
class student:name = Noneage = Nonedef say_hi(self):print(f"大家好,我是{self.name}")
可以看出:
- 类中 不仅可以定义属性用来记录数据
- 类中 也可以定义函数,用来记录行为
其中:
- 类中定义的属性(变量),我们称之为:成员变量
- 类中定义的行为(函数), 我们称之为:成员方法
成员方法的定义语法
在类中定义成员方法和定义函数基本一致,但仍有细微区别:
def 方法名(self, 形参1, …, 形参N):
方法体
可以看出,在方法定义的参数列表中,有一个:self关键字
self关键字是成员方法定义的时候,必须填写的。
- 它可以同来表示类对象自身的意思
- 当我们使用类对象调用方法的时候,self会自动被python传入
- 在方法内部,想要访问类的成员变量,必须使用self
- 在传入参数时候,self是透明的,可以不用理会它
类和对象
** 什么是面向对象:使用对象进行编程**
设计类,基于类创建对象,并使用对象来完成具体的工作
构造方法
Python类可以使用 init()方法,称之为构造方法。
可以实现:
- 在创建类对象(构造器)的时候, 会自动执行
- 在创建类对象(构造器)的时候,将传入参数自动传递给__init__()使用
class Student:name = Noneage = Nonegender = Nonedef __init__(self, name, age, gender):self.name = nameself.age = ageself.gender = genderstu_1 = Student("张三", 18, "女")# 获取对象中记录的信息
print(stu_1.name, stu_1.age, stu_1.gender) # 张三 18 女class Student:def __init__(self, name, age, gender):self.name = nameself.age = ageself.gender = genderstu_1 = Student("张三", 18, "女")# 获取对象中记录的信息
print(stu_1.name, stu_1.age, stu_1.gender) # 张三 18 女
注意:
- 构造方法也是成员方法,不要忘记在参数列表中提供:self
- 在构造方法内定义成员变量,需要使用self关键字
- 这是因为,变量是在定义在构造方法内部,如果要成为成员变量,需要用self来表示
其他内容方法
魔术方法:python把内置的类方法,称之为魔术方法
- init() 构造方法
- str() 字符串方法
- 当类对象需要被转换为字符串输出时,会输入内存地址
- 内存地址没有多大作用,可以通过此方法,控制类转换为字符串的行为
- lt() 大于,小于 符号比较
- 直接对两个对象进行比较是不可以的,但是在类中实现__lt__方法,即可同时完成,小于,大于两个符号比较
- le() 小于等于,大于等于符号比较
- 直接对两个对象进行比较是不可以的,但是在类中实现__le__方法,即可同时完成,小于等于,大于等于两个符号比较
- eq() ==符号比较
- 不实现__eq__方法,对象之间可以进行比较,但是比较内存地址,即: 不同对象==比较一定是False结果
- 实现__eq__方法,就可以按照自己的想法来决定2个对象是否相等了
class Student:name = Noneage = Nonegender = Nonedef __init__(self, name, age, gender):self.name = nameself.age = ageself.gender = genderdef __str__(self):return f"{self.name}:{self.age}:{self.gender}"def __lt__(self, other):return self.age < other.agedef __le__(self, other):return self.age <= other.agedef __eq__(self, other):return self.name == other.namestu_1 = Student("张三", 18, "女")
stu_2 = Student("李四", 16, "男")
# 获取对象中记录的信息
print(stu_1.name, stu_1.age, stu_1.gender) # 张三 18 女
print(stu_1) # 张三:18:女
print(stu_1.__lt__(stu_2)) # False
print(stu_1.__le__(stu_2)) # False
stu_3 = Student("李wu", 16, "男")
print(stu_2.__le__(stu_3)) # True
stu_4 = Student("李四", 16, "男")
print(stu_2.__eq__(stu_4)) # True
封装
面向对象编程:是许多编程语言都支持的一种编程思想
基于模板(类)去创建实体(对象),使用对象完成功能开发
面向对象三大主要特性:
- 封装
- 继承
- 多态
类中提供了私有成员的形式来支持
- 私有成员变量
- 私有成员方法
定义私有成员的方式比较简单
- 私有成员变量:变量名已__开头(两个下划线)
- 私有成员方法:变量名已__开头(两个下划线)
即可完成私有成员的设置
# 定义一个类,包含私有成员变量和方法class Student:__name = None__age = Nonedef __change_name(self, name):self.__name = namestu = Student()
# stu.__name = "张三" # 不报错
print(stu.__name) # 报错
#
stu.__change_name("Lisi") # 报错
私有成员无法被类对象使用,但是可以被其它的成员变使用
继承
class 类名(父类名)
类内容体
- 继承分为:单继承和多继承
- 继承表示:将从父类那里继承来成员变量和成员方法(不含私有 )
单继承
##############单继承
class Phone:IMEI = Noneproducer = "HW"def call_by_4g(self):print("4g")class Phone2023(Phone):face_id = "10001"def call_by_5g(self):print("2023 5G")phone = Phone2023()
print(phone.producer) # HW
print(phone.IMEI) # None
phone.call_by_4g() # 4g
phone.call_by_5g() # 2023 5G
多继承
python的类之间也支持多继承,即一个类,也可以继承多个父类
语法:
class 类名(父类1, 父类2, …, 父类N)
类内容体
##############多继承
class Phone:IMEI = Noneproducer = "HW"def call_by_4g(self):print("4g")class NFC:nfc_type = "第五代"producer = "HW2"def read_card(self):print("nfc读卡")def write_card(self):print("nfc写卡")class RemoteControl:rc_type = "红外"def control(self):print("红外开启")class Phone2023(Phone, NFC, RemoteControl):pass #phone = Phone2023()
print(phone.producer) # HW
print(phone.IMEI) # None
phone.call_by_4g() # 4g
phone.write_card() # nfc写卡
phone.read_card() # nfc读卡
phone.control() # 红外开启
注意事项:
- 单继承:一个类继承一个类
- 多继承:一个类继承多个类,按照顺序向左向右依次继承
- 多继承中,如果父类有同名方法或者属性,先继承的优先级高于后继承
补充:
pass是占位语句,用来保证函数(方法)或者类定义的完整性,表示无内容,空的意思
复写
子类继承父类的成员属性和成员方法后,如果对其不满意,可以进行复写
即:在子类中重新定义同名的属性或者方法即可
##############复写
class Phone:IMEI = Noneproducer = "HW"def call_by_4g(self):print("4g")class Phone2023(Phone):producer = "XIAOMI"def call_by_4g(self):print("4g加速")phone = Phone2023()
print(phone.producer) # XIAOMI
print(phone.IMEI) # None
phone.call_by_4g() # 4g加速
调用父类同名成员
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
如果需要使用被复写的父类的成员,需要特殊的调用方式
方式1:
- 调用父类成员
- 使用成员变量:父类名.成员变量
- 使用成员方法:父类名.成员方法(selg)
方式2:
- 使用super()调用父类成员
- 使用成员变量:super.成员变量
- 使用成员方法:super().成员方法()
注意:只可以在子类内部调用父类的同名成员,子类的实体类对象调用默认的是调用子类复写的
类型注解
变量的类型注解
基本语法:变量:类型
基本数据类型注解
var_1: int = 10
var_2: float = 20.2
var_3: bool = True
var_4: str = “xxxx”
类对象注解
class student:
pass
stu: Student = Student()
基础容器类型注解
my_list: list = [1, 2, 3]
my_tuple: tuple = (1, 2, 3)
my_set: set = {1, 2, 3}
my_dict: dict = {“xxx”: 444}
my_str: str = “xxxxxxx”
容器类型详细注解
my_list: list[int] = [1, 2, 3]
my_tuple: tuple[str, int, bool] = (“xxxx”, 1, True)
my_set: set[int] = {1, 2, 3}
my_dict: dict[str, int] = {“xxx”: 444}
注意:
- 元组类型设置类型详细注解,需要将每个元素都标记出来
- 字典类型设置类型详细注解,需要两个类型,第一个是key,第二个是value
除了使用变量: 类型,也可以在注释中进行类型注解
语法:#type: 类型
在注释中进行类型注解
class Student:
pass
var_1 = random.randint(1, 10) # type: int
var_2 = json.loads(data) # type: dict[str, int]
var_3 = func() # type: Student
- 为变量设置注解,显示的变量定义,一般无需注解
- 一般,无法直接看出变量类型之时会添加变量的类型注解
类型注解的主要功能:
- 帮助第三方工具对代码进行类型的推断,协助代码提示
- 帮助开发者自身对变量进行类型注解
并不会真正对类型做验证和判断,也就是,类型注解仅仅是提示性的,不是决定性的
函数(方法)的类型注解
函数和方法的形参类型注解语法
def 函数方法名(形参名: 类型, 形参名: 类型, …):
pass
返回值注解:
ddef 函数方法名(形参名: 类型, 形参名: 类型, …) -> 返回值类型:
pass
Union类型
from typing import Union
my_list: list[Union[str, int]] = [1, 2, “xxxx”, “cccc”]
my_dict: dict[str, Union[str, ing]] = {“xxx”: “xxx”, “vvv”: 12}
可以使用Union[类型, …, 类型],可以定义联合类型注解
在函数方法注解形参和返回值
def func(data: Union[str, int]) -> Union[str, int]:
pass
多态
指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态
多态常用在继承上:
比如
- 函数(方法)形参声明接受父类对象
- 实际传入父类的子类对象进行工作
即:
- 以父类做定义声明
- 以子类做实际工作
- 用以获得同一行为,不同状态
抽象类:含有抽象方法的类称之为抽象类
抽象方法:方法体是空实现(pass)称之为抽象方法
class Animal:def speak(self):passclass Dog(Animal):def speak(self):print("汪汪汪")class Cat(Animal):def speak(self):print("喵喵喵")
这种设计的含义是:
- 父类用来确定哪些方法
- 具体的方法实现,有子类自己决定
这种写法,就叫抽象类(也称之为接口)
抽象类的作用:
- 多用于顶层设计,以便子类做具体实现
- 也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法。并配合多态使用,获得不同的工作状态
综合案例
python高级技巧
闭包
定义:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们吧这个使用外部函数变量的内部函数称之为闭包。
简单的闭包:
def outer(logo):def inner(msg):print(f"<{logo}>{msg}<{logo}>")return innerfn1 = outer("ccc") # fn1就等于 inner方法
fn1("yyy") # cccyyyccc
fn1("hhh") # hhhyyyhhh
使用nonlocal关键字修饰外部函数的变量,才可以在内部函数中修改它
def outer(num1):def inner(num2):nonlocal num1num1 += num2print(num1)fn1 = outer(10)
fn1(10) # 20
fn1(10) # 30
闭包的好处和缺点:
- 优点:不定义全局变量,也可以让函数持续访问和修改一个外部变量
- 优点:闭包函数引用的外部变量,是外层函数的内部变量。作用域封闭难以被误操作修改,
- 缺点:格外的内存占用
nonlocal关键字的作用:
在闭包函数(内部函数中)想要修改外部函数的变量值需要使用nonlocal声明这个外部变量
装饰器
其实就是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能
基础写法
def sleep():import randomimport timeprint("睡眠中")time.sleep(random.randint(1, 5))### 在调用前,打印我要睡觉了 执行完成后打印我起床了
# 定义一个闭包函数,在闭包方法函数内部
# 1. 执行目标函数
# 2. 并完成功能的添加def outer(func):def inner():print("我要睡觉了")func()print("我起床了")
fn = outer(sleep)
fn()
快捷写法
@outer
def sleep():import randomimport timeprint("睡眠中")time.sleep(random.randint(1, 5))### 在调用前,打印我要睡觉了 执行完成后打印我起床了
# 定义一个闭包函数,在闭包方法函数内部
# 1. 执行目标函数
# 2. 并完成功能的添加def outer(func):def inner():print("我要睡觉了")func()print("我起床了")sleep()
设计模式
单例模式
## 工具类包
class strTools:passstr_tool = StrTools()# 引用文件
from test import str_tools1 = str_tool
s2 = str_tool
## 同一个对象
多线程
threading模块
import time
import threadingdef sing():while True:print("我在唱歌")time.sleep(1)def dance():while True:print("我在跳舞")time.sleep(1)if __name__ == '__main__':sing_thread = threading.Thread(target=sing, name="唱歌线程")dance_thread = threading.Thread(target=dance, name="跳舞线程")sing_thread.start()dance_thread.start()import time
import threadingdef sing(msg):while True:print(msg)time.sleep(1)def dance(msg):while True:print(msg)time.sleep(1)if __name__ == '__main__':sing_thread = threading.Thread(target=sing, name="唱歌线程", args=("我在唱歌",))dance_thread = threading.Thread(target=dance, name="跳舞线程", kwargs={"msg": "我在跳舞"})sing_thread.start()dance_thread.start()
网络编程
正则表达式
递归
https://flet.dev/docs/guides/python/getting-started
peewee 学习(python轻量级操作ORM框架)
fastAPI 学习(python轻量级API框架)
Pydantic,Starlette和FastApi的关系
- Pydantic是一个基于Python类型提示来定义数据验证,序列化和文档(使用JSON模式)库
- Starlette是一种轻量级的ASGI框架/工具包,是构建高性能Asyncio服务的理想选择
from datetime import datetime, date
from typing import List, Optionalfrom pydantic import BaseModel, ValidationError"""
Data validation and settings management using python type annotations
使用python的类型注解来进行数据校验和setting管理pydantic 可以在代码运行时提供类型提示,数据校验失败时提供友好的错误提示定义数据应该如果在纯规范的python代码中保存,并用pydantic验证它
"""class User(BaseModel):id: int # 必填字段name: str = "xxxx" # 有默认值选填字段signup_ts: Optional[datetime] = Nonefriends: List[int] = [] # 列表中元素使int类型或者可以直接转换成int类型 “1”external_data = {"id": "123","signup_ts": "2023-12-12 12:22","friends": [1, 2, "3"]
}user = User(**external_data)
print(user.id, user.name, user.signup_ts, user.friends)
print(repr(user.signup_ts)) # __repr__主要用于调试和开发,而__str__用于为最终用户创建输出。 __repr__打印datetime.datetime(2023, 12, 12, 12, 22)
print(dict(user))# ------------------- 校验失败处理
try:User(id=1, signup_ts=datetime.today(), friends=[1, 2, "not number"])
except ValidationError as e:print(e.json())# --------------------------模型类的属性和方法
print(user.dict)
print(user.json)
print(user.copy) # 这里是浅拷贝
# print(user.parse_obj(obj=external_data))
# print(user.parse_raw())class Sound(BaseModel):sound: strclass Dog(BaseModel):birthday: dateweight: float = Optional[None]sound: List[Sound]
快速上手
# 文件名称main.py
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strprice: floatis_offer: Optional[bool] = None@app.get("/")
def read_root():return {"Hello": "World"}@app.get("/items/{item_id}") # q为?后面参数 /item/1?q="xxx"
def read_item(item_id: int, q: Optional[str] = None):return {"item_id": item_id, "q": q}@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item): # item json参数return {"item_name": item.name, "item_id": item_id}######启动命令:uvicorn main:app --reload # --reload 修改内容服务器会重载
路径参数和数据的解析,验证
from datetime import date
from enum import Enum
from typing import Optional, List
from pydantic import BaseModel, Field
from fastapi import APIRouter, Path, Query, Cookie, Header # path 校验路径参数用app03 = APIRouter()"""
路径参数和数字验证
"""@app03.get("/path/parameters")
def path_params01():return {"message": "This is a message"}@app03.get("/path/{parameters}")
def path_params01(parameters: str): # 函数的顺序就是路由的顺序return {"message": parameters}class CityName(str, Enum):BEI_JING = "beijing china"SHANG_HAI = "shanghai china"@app03.get("/enum/{city}")
async def latest(city: CityName):if city == CityName.SHANG_HAI:return {"city_name": city, "confirmed": 1492, "death": 7}if city == CityName.BEI_JING:return {"city_name": city, "confirmed": 971, "death": 9}return {"city_name": city, "latest": "unknown"}@app03.get("/files/{file_path:path}") # 通过path parameters传递文件路径 添加:path 表示file_path有/ 不作为路径,而是文件路径参数
def print_file_path(file_path: str):return f"the file_path is {file_path}"@app03.get("/path_num/{num}")
def path_params_validate(num: int = Path(None, ge=1, le=10, title="你的数字参数", description="不可描述")):return num
查询参数和数据的解析和验证
"""
查询参数和数据的解析。验证
"""@app03.get("/query")
def page_limit(page: int = 1, limit: Optional[int] = None): # 给了默认值就是选填的参数if limit:return {"page": page, "limit": limit}return {"page": page}@app03.get("/query/bool/conversion")
def type_conversion(param: bool = False): # bool类型转换:yes on 1 True 会转换成Truereturn param@app03.get("query/validations")
def query_params_validate(value: str = Query(..., min_length=8, max_length=16, regex="^a"),values: List[str] = Query(default=["v1", "v2"], alias="alias_name")):return values, value
请求体以及混合参数
"""
请求体和字段
"""class CityInfo(BaseModel):name: str = Field(..., example="beijing") # example是注解的作用,值不会被认证country: strcountry_code: str = None # 给定一个默认值country_population: int = Field(default=800, title="人口数量", description="国家的人口数量", ge=800)class config:schema_extra = {"example": {"name": "beijing","country": "china","country_code": "CN","country_population": 140000000}}@app03.post("/request_boby/city")
def city_info(city: CityInfo):print(city.name, city.country, city.country_code, city.country_population)return city.model_dump_json()
请求体以及混合参数
"""
多参数混合
"""@app03.post("/query_city/{name}")
def mix_city_info(name: str,city01: CityInfo,city02: CityInfo,confirmed: int = Query(ge=0, description="确诊数", default=0),death: int = Query(ge=0, description="死亡数", default=0)
):return city01.model_dump_json(), city02.model_dump_json()
如何定义数据格式嵌套的请求体
"""
数据格式嵌套的请求体
"""class Data(BaseModel):city: List[CityInfo] = None # 这里是定义数据格式嵌套的请求体data: date # 额外的数据类型,还有uuid,datetime, bytes, frozenset,confirmed: int = Field(ge=0, description="确诊数", default=0)deaths: int = Field(ge=0, description="死亡数", default=0)recovered: int = Field(ge=0, description="死亡数", default=0)@app03.post("/query_body/nested")
def nested_models(data: Data):return data
如何设置Cookie和Header参数
"""
Cookie 和 Header 参数
"""
app03.get("/cookie")def cookie(cookie_id: Optional[str] = Cookie(None)): # 定义cookie参数需要使用到Cookie类return {"cookie_id": cookie_id}@app03.get("/header")
def header(user_agent: Optional[str] = Header(None, convert_underscores=True),x_token: List[str] = Header(None, convert_underscores=True)): # convert_underscores 表示是否转换下划线 user_agent -- userAgentreturn {"header": user_agent,"x_token": x_token}
Asynico学习
协程
**概念:**在同一线程内,一段执行代码过程中,可以中断并跳转到另一端代码中,接着之前中断的地方继续执行,协程运行状态于多线程类似。
协程优点:
- 无需线程上下文切换,协程避免了无意义的调度,可以提高性能
- 无需原子操作锁定及同步开销
- 方便切换控制流,简化编程模型
- 高并发 + 高扩展 + 低成本, 一个CPU支持上万的协程不是问题,很适合用于高并发处理
协程缺点:
- 无法利用多核资源,协程本质是单线程,不是同时将单个CPU的多个核用上,协程需要进程配合才能运行在CPU上,
- 进行阻塞操作(如IO时)会阻塞整个程序
实现协程的办法:
- greenlet, 早期模块
- yield 关键字
# def func():
# print("当前是一个生成器函数")
# while True:
# """
# 下面没有代码,会暂停,下一次运行的时候接着上一次运行的位置继续运行
# """
# yield '这是生成器函数返回的数据。。。'
#
#
# obj = func() # 返回一个对象,生成器对象
# print(obj) # <generator object func at 0x000001F85DF34F40><generator object func at 0x000001F85DF34F40>
# print(next(obj)) # 生成器需要通过next()方法进行驱动"""
中断暂停的特点:
在运行第一个任务的使用遇到yield暂停并切换到另一个任务上继续执行
"""# 利用生成器特性进行任务切换
import timedef func_a():while True:print("这是func_a函数")yield time.sleep(0.5)def fun_b(obj):while True:print("这是func_b")obj.__next__()a = func_a()
fun_b(a)
- asyncio修饰器
import asyncio@asyncio.coroutine
def func1():print(1)yield from asyncio.sleep(2) # 遇到IO耗时,自动化切换到tasks中的其他任务print(2)@asyncio.coroutine
def func2():print(3)yield from asyncio.sleep(2) # 遇到IO耗时,自动化切换到tasks中的其他任务print(4)tasks = [asyncio.ensure_future(func1()),asyncio.ensure_future(func2())
]loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
- anync, await
import asyncio
import timeasync def say_after(delay, what):await asyncio.sleep(delay)print(what)async def main():print(f"started at {time.strftime('%X')}")await say_after(1, 'hello')await say_after(2, 'world')print(f"finished at {time.strftime('%X')}")asyncio.run(main())
同步程序与异步程序的执行对比
- 同步:先执行第一个事务,如果遇到阻塞,则进行等待知道一个事务执行完毕,在执行第二个事务
def func():time.sleep(1)now = lambda: time.time()
start = now()for i in range(5):func()print("同步所花费的诗句:%f s" % (now() - start))### 同步所花费的诗句:5.002251 s
- 异步:执行第一个事务之后,如果遇到阻塞,则会执行第二个事务,不会等待,可以通过状态,通知,回调来调用处理结果
async def func(x):await asyncio.sleep(1)print(x)for i in range(5):asyncio.run(func(i))print("异步所花费的诗句:%f s" % (now() - start))
Asynic事件循环
理解成一个死循环,去检测并执行某些代码
任务列表 = [任务1, 任务2, 任务3, ...]while True:可执行的任务列表,已完成的任务列表,去任务列表中检查所有的任务,将可执行和已完成的任务返回for 就绪任务 in 可执行的任务列表:执行已就绪的任务for 完成任务 in 已完成的任务列表:在任务列表中移除已完成任务如果任务列表中的任务都已完成,则终止循环
import asyncio# 去生成或获取一个事件循环
loop = asyncio.get_event_loop()
# 将任务放到任务列表中去
loop.run_until_complete(任务)
快速上手 async
协程函数,定义函数时候,async def 函数名
协程对象,执行协程函数() 得到的协程 对象
async def func():passresult = func()
注意:执行协程函数创建协程对象,函数内部代码不执行
如果想要运行协程函数内部代码,必须要讲协程对象交给事件循环来处理
import asyncioasync def func():print("111")result = func()
# 去生成或获取一个事件循环
loop = asyncio.get_event_loop()
# 将任务放到任务列表中去
loop.run_until_complete(result)
await
await + 可等待对象(协程对象,Future,Task对象 -> IO等待)
import asyncioasync def func():print("1111")response = await asyncio.sleep(2)print("结束", response)asyncio.run(func())
import asyncioasync def others():print("start")await asyncio.sleep(2)print("end")return "返回值"async def func():print("执行协程函数内部代码")# 遇到IO操作挂起当前协程(任务) 等待IO操作完成之后再继续往下执行,当前协程挂起时,事件循环可以去执行其他协程(任务)response = await others()print("IO请求结果结束,结果:", response)asyncio.run(func())
await就是等待对象的值得到结果之后再继续往下走
Task对象
在事件循环中添加多个任务
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式来创建Task对象,这样可以让协程加入事件循环中等待被调度执行,除了使用了asyncio.create_task()函数外,还可以使用低层级的loop.create_task()或者ensuer_future函数,不建议手动实例化Task对象
import asyncioasync def others():print("start")await asyncio.sleep(2)print("end")return "返回值"async def func():print("执行协程函数内部代码")# 遇到IO操作挂起当前协程(任务) 等待IO操作完成之后再继续往下执行,当前协程挂起时,事件循环可以去执行其他协程(任务)response = await others()print("IO请求结果结束,结果:", response)async def main():print("main开始")# 创建Task对象,将当前执行func函数任务添加到事件循环task1 = asyncio.create_task(func())task2 = asyncio.create_task(func())print("main结束")# 当执行某协程遇到的IO操作时,会自动化切换执行其他任务# 此处的await是等待相对应的协程全部执行完毕并获取结果result = await task1resule2 = await task2print(result, resule2)asyncio.run(main())
import asyncioasync def others():print("start")await asyncio.sleep(2)print("end")return "返回值"async def func():print("执行协程函数内部代码")# 遇到IO操作挂起当前协程(任务) 等待IO操作完成之后再继续往下执行,当前协程挂起时,事件循环可以去执行其他协程(任务)response = await others()print("IO请求结果结束,结果:", response)async def main():print("main开始")task_list = [asyncio.create_task(func(), name="task1"),asyncio.create_task(func(), name="task2")]print("main结束")done, pending = await asyncio.wait(task_list, timeout=None) # done 收到一个已经完成的集合,pending是没有完成的集合print(done, pending)asyncio.run(main())
import asyncioasync def others():print("start")await asyncio.sleep(2)print("end")return "返回值"async def func():print("执行协程函数内部代码")# 遇到IO操作挂起当前协程(任务) 等待IO操作完成之后再继续往下执行,当前协程挂起时,事件循环可以去执行其他协程(任务)response = await others()print("IO请求结果结束,结果:", response)task_list = [func(),func()
]done, pending = asyncio.run(asyncio.wait(task_list))
print(done)
Future对象
Task是继承Future, Task对象内部await结果的处理基于Future对象来的
import asyncioasync def main():# 获取当前循环事件loop = asyncio.get_running_loop()# 创建一个任务(Future)对象,这个任务什么都不干fut = loop.create_future()# 等待任务最终结果,没要结果则会一直等下去await futasyncio.run(main())
import asyncioasync def set_after(fut):await asyncio.sleep(2)fut.set_result("6666")async def main():# 获取当前循环事件loop = asyncio.get_running_loop()# 创建一个任务(Future)对象,这个任务什么都不干fut = loop.create_future()# 创建一个任务Task对象,绑定了set_after函数,函数在内部2s之后会给fut赋值# 即手动设置future任务的最终结果,那么fut就可以结束了await loop.create_task(set_after(fut))# 等待任务最终结果,没要结果则会一直等下去date = await futprint(date)asyncio.run(main())
concurrent.futures.Future对象
使用线程池,进程池实现异步操作时用到的对象
import time
from concurrent.futures.thread import ThreadPoolExecutordef func(value):time.sleep(1)print(value)pool = ThreadPoolExecutor(max_workers=5)
# 或者 ProcessPoolExecutor(max_workers=5)for i in range(10):fut = pool.submit(func, i)print(fut)
- 启动调用顺序
- 连接加载日志显示
- 群组显示
- 新功能页面
- 项目封装