Python基础学习

基础语法

字面量

  1. 什么是字面量:
    1. 在代码中,被写下来的固定的值,称为字面量
      | 类型 | 描述 | 说明 |
      | — | — | — |
      | 数组(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) # 结果 11

# 浮点数转字符串
float_str = str(11.11)
print(type(flost_str), flost_str) # 结果 11.11

# 将字符串转成数字
num = int("11")
print(type(num), num) # 结果 11
注意:int("[字符或者中文]") int("11.11") 将会报错 [ValueError: invalid literal for int() with xxxx]

# 将字符串转成float
num_float = float("11.11")
print(type(num_float), num_float) # 结果 11.11
注意:float("11") 将输入 11.0

# 整数转浮点数
float_num = float("11")
输出11.0

# 浮点数转整型(会丢失精度)
int_num = int(11.11)
输入 11

标识符

  • 变量名字
  • 方法的名字
  • 类的名字
  • 等等

这些名字,统一称之为标识符,用来做内容的标识;
python,标识符命名规则:

  • 内容限定
    • 标识符名称中 只允许出现,这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.352个空格补足宽度,小数点部分限制2位精度,四舍五入为.35
    %.2f: 表示不限制宽度,只设置小数点精度为2,如11.345设置后为11.35
  • 字符串格式化方式2(快速格式化)
    • 通过语法 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 = 7

print(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 TODO
	if 条件2:
        满足条件2, TODO
第二个if 属于第一个if内,只有第一个if满足条件,才会执行第二个if
嵌套的关键点,在于:空格缩进
通过空格缩进,来决定语句之间的层次关系
  • 嵌套判断语句可以使用于多条件,多层次的逻辑判断
  • 嵌套判断语句可以根据需求,自由组合if elif else 来构建多层次判断
  • 嵌套判断语句,一定要注意空格缩进,Python通过空格缩进来决定层次关系

实战案例

import random

num = 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 += i
    i += 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 = False
    else:
        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 = 1
    while j <= i:
        print(f"{j} * {i} = {i * j}\t", end="")
        j += 1
    i += 1
    print()

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 = i
    for 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 random

all_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("工资发完了,下个月再说")
            break
        all_price -= 1000
        print(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,其类型是:
无返回值的函数,实际上就是返回了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 = 100

def change_num2():
    global num
    num = 300

change_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 money
    if flag == 1:
        money += use_money
    else:
        money -= use_money
    print(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()
            break
        else:
            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 list


my_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)   # 4

my_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'] demo8

my_list.remove("demo2")
print(my_list)  # ['demo1', 'demo3', 'demo5', 'demo4', 'demo6', 'demo7']

num = my_list.count("demo1")
print(num)  # 1

my_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 = "男")
	TODO

user_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):
    TODO

test_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临时变量,就记录了文件的一行数据
  • 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))  # 
# 读取文件 --- 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))  # 
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))  # 
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模式下,文件存在会在最后追加写入文件

文件操作综合案例

Python基础学习_第1张图片
Python基础学习_第2张图片

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] == "测试":
                continue
        fw.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 / 0
    print("这是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 / 0
    print("这是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 fun02
    fun01()
  File "D:\Desktop\pySpace\py-demo\exceptiondemo.py", line 3, in fun01
    num = 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 time

time.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_module

my_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

综合案例

Python基础学习_第3张图片

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_util

if __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 = None
    age = None
    gender = 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 = None
    age = None

    def say_hi(self):
        print(f"大家好,我是{self.name}")

可以看出:

  • 类中 不仅可以定义属性用来记录数据
  • 类中 也可以定义函数,用来记录行为

其中:

  • 类中定义的属性(变量),我们称之为:成员变量
  • 类中定义的行为(函数), 我们称之为:成员方法

成员方法的定义语法

在类中定义成员方法和定义函数基本一致,但仍有细微区别:

def 方法名(self, 形参1, …, 形参N):
方法体

可以看出,在方法定义的参数列表中,有一个:self关键字
self关键字是成员方法定义的时候,必须填写的。

  • 它可以同来表示类对象自身的意思
  • 当我们使用类对象调用方法的时候,self会自动被python传入
  • 在方法内部,想要访问类的成员变量,必须使用self
  • 在传入参数时候,self是透明的,可以不用理会它

类和对象

** 什么是面向对象:使用对象进行编程**
设计类,基于类创建对象,并使用对象来完成具体的工作

构造方法

Python类可以使用 init()方法,称之为构造方法。
可以实现:

  • 在创建类对象(构造器)的时候, 会自动执行
  • 在创建类对象(构造器)的时候,将传入参数自动传递给__init__()使用
class Student:
    name = None
    age = None
    gender = None

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


stu_1 = Student("张三", 18, "女")

# 获取对象中记录的信息
print(stu_1.name, stu_1.age, stu_1.gender)  # 张三 18 女

class Student:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


stu_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 = None
    age = None
    gender = None

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def __str__(self):
        return f"{self.name}:{self.age}:{self.gender}"

    def __lt__(self, other):
        return self.age < other.age

    def __le__(self, other):
        return self.age <= other.age

    def __eq__(self, other):
        return self.name == other.name


stu_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 = None

    def __change_name(self, name):
        self.__name = name


stu = Student()
# stu.__name = "张三" # 不报错
print(stu.__name)  # 报错
#
stu.__change_name("Lisi") # 报错

私有成员无法被类对象使用,但是可以被其它的成员变使用

继承

class 类名(父类名)
类内容体

  • 继承分为:单继承和多继承
  • 继承表示:将从父类那里继承来成员变量和成员方法(不含私有 )

单继承

##############单继承
class Phone:
    IMEI = None
    producer = "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 = None
    producer = "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 = None
    producer = "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):
        pass
    
class 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 inner

fn1 = outer("ccc") # fn1就等于 inner方法
fn1("yyy") # cccyyyccc
fn1("hhh")  # hhhyyyhhh

使用nonlocal关键字修饰外部函数的变量,才可以在内部函数中修改它

def outer(num1):
	def inner(num2):
        nonlocal num1
        num1 += num2
        print(num1)

fn1 = outer(10)
fn1(10)  # 20
fn1(10)  # 30

闭包的好处和缺点:

  • 优点:不定义全局变量,也可以让函数持续访问和修改一个外部变量
  • 优点:闭包函数引用的外部变量,是外层函数的内部变量。作用域封闭难以被误操作修改,
  • 缺点:格外的内存占用

nonlocal关键字的作用:
在闭包函数(内部函数中)想要修改外部函数的变量值需要使用nonlocal声明这个外部变量

装饰器

其实就是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能

基础写法

def sleep():
    import random
    import time
    print("睡眠中")
    time.sleep(random.randint(1, 5))

### 在调用前,打印我要睡觉了 执行完成后打印我起床了
# 定义一个闭包函数,在闭包方法函数内部
# 1. 执行目标函数
# 2. 并完成功能的添加

def outer(func):
    def inner():
        print("我要睡觉了")
        func()
        print("我起床了")
fn = outer(sleep)
fn()

快捷写法

@outer
def sleep():
    import random
    import time
    print("睡眠中")
    time.sleep(random.randint(1, 5))

### 在调用前,打印我要睡觉了 执行完成后打印我起床了
# 定义一个闭包函数,在闭包方法函数内部
# 1. 执行目标函数
# 2. 并完成功能的添加

def outer(func):
    def inner():
        print("我要睡觉了")
        func()
        print("我起床了")

sleep()

设计模式

单例模式

## 工具类包
class strTools:
    pass

str_tool = StrTools()

# 引用文件
from test import str_tool

s1 = str_tool
s2 = str_tool 
## 同一个对象

多线程

threading模块

Python基础学习_第4张图片

import time
import threading

def 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 threading

def 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, Optional

from 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] = None
    friends: 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: str


class Dog(BaseModel):
    birthday: date
    weight: float = Optional[None]
    sound: List[Sound]

快速上手

# 文件名称main.py
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float
    is_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 会转换成True
    return 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: str
    country_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 -- userAgent
    return {
        "header": user_agent,
        "x_token": x_token
    }

Asynico学习

协程

**概念:**在同一线程内,一段执行代码过程中,可以中断并跳转到另一端代码中,接着之前中断的地方继续执行,协程运行状态于多线程类似。
协程优点:

  • 无需线程上下文切换,协程避免了无意义的调度,可以提高性能
  • 无需原子操作锁定及同步开销
  • 方便切换控制流,简化编程模型
  • 高并发 + 高扩展 + 低成本, 一个CPU支持上万的协程不是问题,很适合用于高并发处理

协程缺点:

  • 无法利用多核资源,协程本质是单线程,不是同时将单个CPU的多个核用上,协程需要进程配合才能运行在CPU上,
  • 进行阻塞操作(如IO时)会阻塞整个程序

实现协程的办法:

  • greenlet, 早期模块
  • yield 关键字
# def func():
#     print("当前是一个生成器函数")
#     while True:
#         """
#         下面没有代码,会暂停,下一次运行的时候接着上一次运行的位置继续运行
#         """
#         yield '这是生成器函数返回的数据。。。'
# 
# 
# obj = func()  # 返回一个对象,生成器对象
# print(obj)   # 
# print(next(obj))   # 生成器需要通过next()方法进行驱动


"""
中断暂停的特点:
在运行第一个任务的使用遇到yield暂停并切换到另一个任务上继续执行
"""

# 利用生成器特性进行任务切换
import time


def 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 time

async 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():
    pass

result = func()

注意:执行协程函数创建协程对象,函数内部代码不执行
如果想要运行协程函数内部代码,必须要讲协程对象交给事件循环来处理

import asyncio

async def func():
	print("111")

result = func()
# 去生成或获取一个事件循环
loop = asyncio.get_event_loop()
# 将任务放到任务列表中去
loop.run_until_complete(result)

await

await + 可等待对象(协程对象,Future,Task对象 -> IO等待)

import asyncio

async def func():
    print("1111")
    response = await asyncio.sleep(2)
    print("结束", response)

asyncio.run(func())
import asyncio


async 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 asyncio


async 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 task1
    resule2 = await task2

    print(result, resule2)

asyncio.run(main())
import asyncio


async 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 asyncio


async 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 asyncio


async def main():
    # 获取当前循环事件
    loop = asyncio.get_running_loop()

    # 创建一个任务(Future)对象,这个任务什么都不干
    fut = loop.create_future()

    # 等待任务最终结果,没要结果则会一直等下去
    await fut

asyncio.run(main())
import asyncio


async 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 fut
    print(date)


asyncio.run(main())

concurrent.futures.Future对象

使用线程池,进程池实现异步操作时用到的对象

import time
from concurrent.futures.thread import ThreadPoolExecutor


def 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)
  • 启动调用顺序
  • 连接加载日志显示
  • 群组显示
  • 新功能页面
  • 项目封装

你可能感兴趣的:(python,学习,windows)