当前位置: 首页 > news >正文

长沙网站设计开发阿里云小程序开发

长沙网站设计开发,阿里云小程序开发,php mysql 网站源码,php企业网站源码推荐python从入门到进去 第一章、软件和工具的安装一、安装 python 解释器二、安装 pycharm 第二章、初识 python一、注释可分三种二、打印输入语句三、变量1、基本数据类型1.1、整数数据类型 int1.2、浮点数数据类型 float1.3、布尔数据类型 boolean1.4、字符串数据类型 string 2、… python从入门到进去 第一章、软件和工具的安装一、安装 python 解释器二、安装 pycharm 第二章、初识 python一、注释可分三种二、打印输入语句三、变量1、基本数据类型1.1、整数数据类型 int1.2、浮点数数据类型 float1.3、布尔数据类型 boolean1.4、字符串数据类型 string 2、组合数据类型2.1、列表数据类型list2.2、元组数据类型tuple2.3、字典数据类型dictionary dict java中的map2.4、集合数据类型set 3、序列的推导式3.1、列表的推导式3.2、字典的推导式3.3、集合的推导式3.4、元组的推导式 4、变量的作用域 四、运算符与表达式1. 算术运算符2. 赋值运算符3. 比较关系运算符4. 逻辑运算符5. 位运算符6. 成员运算符7. 身份运算符8. 运算符的优先级 第三章、基础语法一、条件判断1. 单分支判断2. 双分支判断3. 多分支判断4. match 匹配不同的值运行相应的逻辑相当于java中的switch 二、循环语句1、for循环2、while循环3. break 跳出距离最近的循环4. 与 else 的结合5. continue 跳过本次最近循环执行下次循环6. pass 三、异常1、常见的异常类型2、try-except语句3、与 else 语句结合当没有出现异常的时候执行3、 finally 表示一定会执行的代码4、 raise 手动抛出一个异常 四、函数1、函数的定义2、函数的调用3、函数的参数3.1、位置形参3.2、默认形参缺省形参3.3、可变参数3.4、参数控制 4、函数的返回值5、匿名函数6、闭包7、装饰器相当于Java的代理模式7.1、使用装饰器7.2、自定义函数装饰器7.3、还可以自定义带有参数的装饰器7.4、类装饰器 五、模块1、模块的简单使用2、import 语句3、from … import 语句4、__name__属性5、包6、\_\_init__.py 的 \_\_all__ 六、迭代器与生成器1、可迭代对象2、迭代器3、生成器 第四章、面向对象一、类1、类的创建2、类的属性——变量3、类的属性——类函数4、类的属性——静态函数5、类的属性——对象函数6、类中函数的调用权限7、类的属性的增删改查 二、对象1、对象的创建2、对象的使用3、对象的属性和函数 三、基础魔术函数1、_\_init__(self)2、_\_del__(self)3、_\_new__(cls, *args, **kwargs)3、_\_repr__(self) 和 _\_srt__(self)4、_\_doc__ 表示类的注释信息5、_\_doc__ 表示当前操作的对象在哪个模块6、_\_doc__ 表示当前操作的对象的类是哪个7、_\_call__(self, *args, **kwargs)表示通过 “对象”来调用的方法8、_\_dict__查看类或者对象的所有属性9、\_\_getitem__ \_\_setitem__ \_\_delitem__ 让对象有字典的功能 四、封装1、私有属性2、获取私有属性3、修改私有属性 五、继承1、如何继承2、继承之后的使用3、继承之重写和扩展4、子类调用父类的属性5、多继承6、继承的传递 六、单例模式1、通过 \_\_new__ 实现2、通过 classmethod 类方法实现3、通过装饰器来实现3、通过元类来实现 第五章、文件读写一、文件的打开二、文件的读取三、文件的写入四、文件的关闭五、打开一个可读可写的文件六、文件目录的简单操作-os模块1、文件重命名操作-os.rename(原名,新名)2、创建文件夹-os.mkdir(目录地址)3、删除文件夹-os.rmdir(目录地址)4、删除文件-os.remove(目录地址)或os.unlink(目录地址)5、获取当前路径-os.getcwd()6、切换当前路径-os.chdir(切换命令)7、获取路径下的目录列表-os.listdir(指定路径)8、更多用法 第六章、多线程一、线程1、线程的创建与启动2、线程的同步与锁3、线程间通信4、线程池5、自定义创建新线程 二、进程1、进程的创建与启动2、进程属性与方法3、进程间的通信与同步4、进程池Pool5、示例代码 三、协程1、greenlet创建协程2、gevent创建协程3、asyncio创建协程 四、进程-线程-协程之前的异同1、相同点2、不同点 第一章、软件和工具的安装 一、安装 python 解释器 通过 官网 下载 python 光标放在 Downloads 上选择自己的系统点击右边的软件进行下载 二、安装 pycharm 打开 官网 下载软件 选择自己系统的工具下啦到最后选择免费版 下载之后傻瓜式安装就行了 第二章、初识 python 一、注释可分三种 # 这是一行注射 printhello word# 行内注释这是多行注释二、打印输入语句 输出函数 print( *objects, sep ’ , end ‘\n’ , file None , Flush False) 参数解释*objects可变参数可以输出多个入参用 “,” 分开*sep表示用 “,” 分开中间插入的部分 默认空格end输出语句最后添加的部分默认\n换行 print(2024,我要,发大财sep,end!!!\n)格式化输出 格式化字符解释%s字符串占位符%d整数占位符 ,%02d 表示显示整数显示两位以上不够用0填补%f浮点数占位符%0.2f表示保留小数点后两位四舍五入%%输出% s 字符串 i 3 f 2.34 print(%s是字符串%f是个整数的数字%0.1f是个保留一位小数点的值 % s,i,f)输入函数 input() 默认接收的都是str字符串使用 函数Type() 可以查看 a input(请输出你想打印的话) print(a,类型为,Type(a))如果需要转换可以使用类型转换函数比如转换为int类型进行计算 int() a input(请输出你想打印的一个数) a int(a) 1 print(这个数比较你输入的大,a)三、变量 1、基本数据类型 类型解释int整数类型可通过int()转换float浮点数类型可以通过float()转换bool布尔类型可以通过bool()转换,00.0None或者空容器为FalseTrue和False首字母大写str字符串类型可以通过str()转换 # 定义一个变量 a 1 # 定义多个变量同一个值 a b c d 100 # 定义多个变量不同的值 a,b,c,d 1,2,3,4变量可以通过重新赋值进行修改不受类型的限制 a str print(我是字符串aa) a 1 print(我是整数aa) a 1.4 print(我是浮点数aa)1.1、整数数据类型 int 整数的创建有两种方式 # 第一种方式 通过 直接创建 i1 3 # 第二种方式通过 int() 创建 i2 int(2.9) print(i2) # 2i2 int(4) print(i2) # 4i2 int(4.4) print(i2) # 异常 ValueErrorpython设置了一些小整数保存在内存中-5 - 256 。 在使用这个范围内的整数的时候这些整数是的内存地址是相等的可以通过 id() 查看 1.2、浮点数数据类型 float 整数的创建有两种方式 # 第一种方式通过 直接创建 f1 0.1 # 第二种方式通过 float() 创建 f2 float(0.2) print(f2) # 0.2f2 float(1) print(f2) # 1.0浮点类型的数在进行计算的时候会出现丢失精度的问题 f1 ,f2 0.1 ,0.24 print(f1 f2) # 0.33999999999999997可以通过 round(number, ndigits) 进行四舍五入的计算number表示参与计算的值ndigits表示保留几位小数点 f1 ,f2 0.1 ,0.24 print(round((f1 f2),2)) # 0.34也可以通过引入 math 包 利用 math的向下取整和向上取整来取值 import math # 引入math 包 f1 ,f2 3.1 ,0.24 # 向上取整 print(math.ceil(f1 f2)) # 4 # 向下取整 print(math.floor(f1 f2)) # 31.3、布尔数据类型 boolean bool类型有两种创建方式 # 第一种方式通过 直接创建 b True # 第二种方式通过 bool()创建 pringbool(0) # False print(bool(0.00)) # False print(bool(1)) # True print(bool(2)) # True print(bool(True)) # True print(bool(False)) # True print(bool(abc)) # True print(bool()) # False print(bool( )) # TrueFalse的值有None、0、0.0、False、所有的空容器集合list字符串元祖字典 1.4、字符串数据类型 string 1.4.1、字符串的创建有三种方式 # 通过双引号或者单引号单行创建 s1 hello word # 通过str()创建 s2 str() # 通过三引号多行创建 s3 hello word !1.4.2、字符串的转义字符 \ s1 小明说\早上好\print(s1) # 小明说早上好如果不想让字符串中的斜杠进行转义可以在字符串前面加一个 r s1 abc\fgd s2 rabc\fgdprint(s1) print(s2)# 输出 abcgd abc\fgd1.4.3、字符串的加法就是把两个字符串加到一起不能加其他数据类型会报 TypeError 异常 s1 hello s2 word print(s1 s2) # helloword print(s1 100) # 报异常1.4.4、字符串的乘法就是把字符串乘多个然后加起来只能乘整数类型否则会报 TypeError 异常 s1 hello print(s1 * 5) # hello hello hello hello hello 1.4.5、字符串的索引 字符串会为每一个字符从左到右起一个索引从 0 开始也可以从右往左从 -1 开始 s hello,word print(s[0],s[-4]) # h w取值的时候如果索引超过字符串范围会报 IndexError 异常 1.4.6、切片运算一个左闭右开的范围取值运算可以设置步长取下一个多远的值 s hello,word print(s[2:5]) # llo# 设置步长为2 s 123456789 print(s[1::2]) # 2468步长可以设置为负数表示从右往左取值 s 123456789#反转字符串 print(s[::-1]) # 9876543212、组合数据类型 类型解释list 列表使用 []定义中间用逗号分隔有索引从0开始可修改长度和值tuple 元组使用 ()定义与列表类似区别是元素不能修改string 字符串字符串也是组合数据的一种dict 字典dict字典是无序的对象集合相当于java中的map。用{}定义可以用于保存健值对中间用 : 隔开健是唯一的只能是字符串、数字或者元组。值可以是任何数据类型可重复set 集合无序不能重复取交集并集用的也是 {} 定义 2.1、列表数据类型list 2.1.1、列表的创建分为两种方式列表中的元素可以是任意类型的数据 # 第一种方式 通过 [] 的方式创建 l [1,2,3,4,a,True,4.5]# 第二种方式 通过 list() 创建 l1 list(123456abc) # 只支持 string类型 print(l1) # [1,2,3,4,5,6,a,b,c]list(5) # 会报 TypeError 异常2.1.2、列表的元素 列表和str字符串类似可以通过索引取值而且列表中的值还可以修改 l [1,2,3,4,5,6] print(l[2]) # 3 l[3] 9 print(l) # [1,2,3,9,5,6]2.1.3、列表的常用操作 两个列表相加就是把两个列表中的元素放到一个列表中 l1 l2 [1,2,3] , [3,4,5,6] print(l1 l2) # [1,2,3,3,4,5,6] print(l1 3) # 报 TypeError 异常列表的乘法运算就是列表中的元素个数乘积放一个列表中 l [1,2,3] print(l * 3) #[1,2,3,1,2,3,1,2,3] print(l * 3.2) # 报 TypeError 异常包含元素判断 in 和 not in l [1,2,3] #判断列表 l 中是否包含 3 print(3 in l ) # True print(3 in l ) # False # 判断列表 l 中是否包不含 4 print(4 not in l) # True列表的大小的比较是从左往右依次比较大小如果想到则比较下一个元素 l1 l2 [1,2,3] , [1,3] print(l1 l2) # True # 先比较第一个元素 l1[0]1,l2[0]1,想到比较第二个元素l1[1]2,l2[1]3,因为23所以 l1l2获取列表的长度len()最大值max()最小值min()。 l [1,2,3,6] print(len(l)) #4 print(max(l)) #6 print(min(l)) #1列表的切片操作 l [1,2,3,4,5,6] print(l[::2]) # [1, 3, 5] print([1,2,3,4,5,6][-1:1:-1]) # [6, 5, 4, 3]删除列表 del l [1,2,3,6] del l列表的遍历使用 while 或者 for 可以使用 enumerate() 同时遍历列表的索引和值 l [1,2,3,6] i 0 while i len(l):print(l[i])i1 # 或者 for i in l:print(i) # 打印结果为 # 1 # 2 # 3 # 6 for k , v in enumerate(l):print(k,v) # 打印结果为 # 0 1 # 1 2 # 2 3 # 3 6介绍一下 range() 迭代器系统提供的一个内置函数,它有三个入参range(start,end,[step1])生成一个左闭右开的等差序列start开始的索引包括索引本身默认为0可不填end截止的索引不包含索引本身step步长默认为1可不填-1表示倒叙。生成的序列不能被修改 print(list(range(10))) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(list(range(1,10))) # [1, 2, 3, 4, 5, 6, 7, 8, 9]print(list(range(1,10,2))) # [1, 3, 5, 7, 9]print(list(range(10,0,-1))) # [10, 8, 6, 4, 2]因为range是个迭代器无法直接打印所以转换成list方便展示其成员。 range一般都是和 for - in 结合遍历使用 l [1,2,3,4,5,6,7] for i in range(0,len(l),2):print(i,:,l[i]) # 输出为 0 : 1 2 : 3 4 : 5 6 : 72.1.4、列表的常用方法 方法名介绍pop(a)取出指定a索引的值集合的长度 -1 ,有返回值index(a)查找入参a在集合中的索引从左到右取第一个如果不在会报 ValueError 异常remove(a)删除集合中值为a的元素从左到右删除第一个如果没有会报 ValueError 异常copy(a)拷贝集合append(a)集合添加a元素到尾部extend([a,b])添加列表中的元素a和b到列表中可以是str类型insert(a,b)插入一个元素b放到指定的a索引位置需要两个入参count(a)获取集合中指定元素的个数reverse()没有入参和反参反转列表sort()没有入参和反参对列表进行排序列表中的元素类型为intfloat,boole可以一起比较不能和str一起比较会报TypeError元素全部是str的可以排序clear()没有入参和反参清空列表中的所有元素 l [1,2,3,4,5,6,7,8,9]print(集合的值为%s ,长度为:%d ,类型为%s % (l, len(l), type(l)) ) # 集合的值为[1, 2, 3, 4, 5, 6, 7, 8, 9] ,长度为:9 ,类型为class listi0 l.pop(0) # 取出指定索引的值集合的长度 -1 ,有返回值 print(集合的值为%s ,长度为:%d取出的值为%s % (l, len(l),i0) ) # 集合的值为[2, 3, 4, 5, 6, 7, 8, 9] ,长度为:8取出的值为1i1 l.index(3) # 查找入参在集合中的索引如果不在会报 ValueError 异常 print(集合的值为%s ,长度为:%d返回值为%s % (l, len(l),i1) ) # 集合的值为[2, 3, 4, 5, 6, 7, 8, 9] ,长度为:8返回值为1rl l.remove(3) # 删除集合中指定value的元素从左到右删除第一个如果没有会报 ValueError 异常 print(集合的值为%s ,长度为:%d,返回值为%rl % (l, len(l),rl) ) # 集合的值为[2, 4, 5, 6, 7, 8, 9] ,长度为:7,返回值为Nonelcpl l.copy() # 拷贝集合 print(集合的值为%s ,长度为:%d ,类型为%s % (cpl, len(cpl), type(cpl)) ) # 集合的值为[2, 4, 5, 6, 7, 8, 9] ,长度为:7 ,类型为class listla l.append(500) # 集合添加元素到尾部 print(集合的值为%s返回值为%s % (l,la)) #集合的值为[2, 4, 5, 6, 7, 8, 9, 500]返回值为Nonele l.extend([3,4,5]) # 添加列表中的元素到列表中 print(l,le) # [2, 4, 5, 6, 7, 8, 9, 500, 3, 4, 5] Nonele l.extend(34) # 添加 3 , 4 到列表中 print(l,le) # [2, 4, 5, 6, 7, 8, 9, 500, 3, 4, 5, 3, 4] Noneli l.insert(3,1) # 插入数字 1 到索引为 3 的位置上 print(l,li) # [2, 4, 5, 1, 6, 7, 8, 9, 500, 3, 4, 5, 3, 4] Nonelc l.count(4) # 获取列表中存在元素 4 的个数 print(l,lc) # [2, 4, 5, 1, 6, 7, 8, 9, 500, 3, 4, 5, 3, 4] 2lr l.reverse() # 反转列表 print(l,lr) # [4, 3, 5, 4, 3, 500, 9, 8, 7, 6, 1, 5, 4, 2] Nonel [3,8,5,40,5,3,17,1,4.5,True] ls l.sort() # 对列表进行排序需要列表中的数据类型相同 print(l,ls) # [1, True, 3, 3, 4.5, 5, 5, 8, 17, 40] None l [3,8,5,40,5,3,17,1,4.5,True,a] ls l.sort() print(l,ls) # 会报 TypeError 异常l [3,2,0.1] ls l.sort() # 全是 str 数据类型的列表才可以排序不报错 print(l,ls) # [0.1, 2, 3] Nonelc l.clear() # 清空列表中的所有元素 print(l,lc) # [] None2.2、元组数据类型tuple 2.2.1、元组的创建分两种方式 # 第一种方式 通过 () 直接创建 t (1,2) print(t,type(t)) # (1,2) class tuple需要注意的是通过小括号创建当只有一个元素的时候需要自元素后边加一个逗号因为当只有一个元素的时候默认会当成int类型t (1) print(t,type(t)) # 1 class int t (1,) print(t,type(t)) # (1,) class tuple# 第二种方式 通过使用 tuple() 进行创建入参只有一个只能容器类的数据str列表元组等 t tuple(123456) print(t,type(t)) # (1, 2, 3, 4, 5, 6) class tuple2.2.2、元组的常用操作 元组和列表的区别是元素不能修改 t (0,1,2,3,4) print(t[2]) # 2 t[2] 20 # 会报 TypeError 异常元组也是支持加法和乘法的 t1 (1,2,3) t2 (3,4,5) print(t1 t2) # (1, 2, 3, 3, 4, 5) print(t1 * 2) # (1, 2, 3, 1, 2, 3)元组的常用操作方法只有两个count和index t (0,1,2,30,4,3) ti t.index(3) # 获取元素是3第一个索引 print(t,ti)tc t.count(4) # 取元素4在元组中的个数 print(t,tc)2.3、字典数据类型dictionary dict java中的map 2.3.1 字典的创建分两种一种是使用 {} 一种是使用 dict() d1 {1:a,2:list(abcd)}d3 dict(a1,b2) d4 dict([(c,4),(d,5)])2.3.2 字典的增删改查 d1 {1:a,2:list(abcd),3:hn,4:185}# 通过key 查 value v4 d1[4] # 或者 v4 d1.get(4)# 新增 d1[5] 5的值 d1[(tuple,类型的key)] {字典类型:的值}# 修改 d1[4] 4号新值 # 或者使用update方法(也可以新增元素 d2.update({4:4号新值})# 删除元素 d1.pop(4)# 清空元素 d1.clear()# 删除字典 del d1字段的 in 方法判断的是 key 是否存在 2.3.3 字典的遍历 # 遍历 kv 简直对 for k,v in d1.items():print(k,v)# 遍历所有的 key for i in d1:print(i,d1[i]) # 或者 for k in d1.keys():print(k)# 遍历所有的 value for v in d1.values():print(v)2.3.4 字典的常用方法 items() 变成kv健值对类型 d2 {1:11,2:22} print(d2) print(d2.items())# 输出 {1: 11, 2: 22} dict_items([(1, 11), (2, 22)])pop() 弹出指定k 的元素弹出之后字典就没有这个元素了 d {1:11,2:22,3:33,4:44} d.pop(4)popitem() 从队尾弹出一个元素 d {1:11,2:22,3:33,4:44} d.popitem()copy() 拷贝出一个一摸一样的字典 d3 d2.copy()2.4、集合数据类型set 2.4.1 集合的创建有两种形式使用{} 和 set() s1 {a,b,c,3,4,5} s2 set(123) # 通过字符串创建 s3 set([10,20,30]) # 通过列表创建 s4 set((1,3,4)) # 通过元组创建 s5 set({name:大宝age:18}) # 通过字典创建,取 key 值2.4.2 集合的遍历 for i in s3:print(i)2.4.3 集合的常用方法 add() 添加一个元素 s1.add(add)remove() 删除指定的元素 s1.remove(add)pop() 从头部弹出一个元素 s1.pop()2.4.4 取交集和并集 s1 {a,b,c,3,4,5}s2 set(123adc)s3 s1 s2 s4 s1 | s2print(s3) print(s4)# 输出{a, c} {b, 3, 4, 5, 1, a, 3, 2, c, d}3、序列的推导式 Python 推导式是一种独特的数据处理方式可以从一个数据序列构建另一个新的数据序列的结构体。 Python 推导式是一种强大且简洁的语法适用于生成列表、字典、集合和生成器。 3.1、列表的推导式 列表推导式格式为 新列表 [元素的操作 for 变量 in 旧列表 if 筛选]旧列表可以是列表、元组、或者集合 其中 if 筛选 不是必须有的如果写了则不符合 if 筛选 的元素不会进入新的列表中举例 names [ra,ab,abc,abcd,abcde]# 过滤掉长度小于等于2的字符串并将剩余的字符串加上 .test new_names1 [name.test for name in names if len(name) 2] print(new_names1)# 过滤掉长度小于等于3的字符串并将剩余的字符串转换成大写字母 new_names2 [name.upper() for name in names if len(name) 3] # upper() 字符串转换成大写 print(new_names2)# 输出 [abc.test, abcd.test, abcde.test] [ABCD, ABCDE] 格式二 新列表 [结果1 if 判断条件 else 结果2 for 变量 in 旧列表]这种格式不会丢弃元素只不过是通过不同的判断条件执行不同的结果放到新列表中举例 names (ra,ab,abc,abcd,abcde)# 长度大于2的字符串前边加上 “结果1”否则前面加上 “结果2”放入新列表中 new_name [结果1name if len(name) 2 else 结果2name for name in names ] print(new_name)#输出[结果2ra, 结果2ab, 结果1abc, 结果1abcd, 结果1abcde]3.2、字典的推导式 格式为 新字典 {k:v for 变量 in 旧列表 if 筛选} # if 筛选 非必须举例 names {ra,ab,abc,abcd,abcde} # 过滤出长度大于2的字符串生成 key 是字符串本身value 是字符串长度的字典 name_dict {name:len(name) for name in names if len(name) 2} print(name_dict)# 输出 {abcde: 5, abc: 3, abcd: 4}3.3、集合的推导式 格式是 # 格式一 新集合 {元素的操作 for 变量 in 旧列表 if 筛选} # if 筛选 非必须 # 格式二 新集合 {结果1 if 判断条件 else 结果2 for 变量 in 旧列表}举例 names {ra,ab,abc,abcd,abcde} # 筛选出以 ”a“ 开头的字符串并乘以2组成新的集合 name_set {name*2 for name in names if name.startswith(a)} print(name_set)# 已 ”a“ 开头的字符串乘以2否则乘以3组成新的集合 name_set2 {name*2 if name.startswith(a) else name*3 for name in names} print(name_set2)# 输出{abcdeabcde, abcabc, abcdabcd, abab} {abcdeabcde, abab, rarara, abcdabcd, abcabc}3.4、元组的推导式 元组推导式和列表推导式的用法也完全相同只是元组推导式是用 () 圆括号将各部分括起来而列表推导式用的是中括号 []另外元组推导式返回的结果是一个生成器对象。需要使用 tuple() 转换成元组 格式是 # 格式一 新元组生成器 (元素的操作 for 变量 in 旧列表 if 筛选) # if 筛选 非必须 # 格式二 新元组生成器 (结果1 if 判断条件 else 结果2 for 变量 in 旧列表)举例 names [ra,ab,abc,abcd,abcde]# 筛选出以 ”a“ 开头的字符串并乘以2组成新的元组 name_tuple1 (name*2 for name in names if name.startswith(a)) print(name_tuple1,》,tuple(name_tuple1))# 以 ”a“ 开头的字符串乘以2否则乘以3组成新的元组 name_tuple2 (name*2 if name.startswith(a) else name*3 for name in names) print(name_tuple2,》,tuple(name_tuple2))# 输出generator object genexpr at 0x0000029E98C7A8E0 》 (abab, abcabc, abcdabcd, abcdeabcde) generator object genexpr at 0x0000029E98C7A9B0 》 (rarara, abab, abcabc, abcdabcd, abcdeabcde)4、变量的作用域 变量分为全局变量和局部变量两种 全局变量在类中设置的变量函数中和函数外都能使用 局部变量在函数中设置的变量只能在函数中使用 a 10 # 全局变量def f():a 100 # 局部变量print(局部变量a,a)f() print(全局变量a,a)# 输出 全局变量a 10 局部变量a 100函数中设置相同名字的局部变量是不会修改全局变量的如果想要修改全局变量则可以通过关键字 global 变量名 先声明在本函数中使用的是全局变量 a 10 # 全局变量def f():global a # 声明在这个函数中使用的a 是全局变量print(函数中的全局变量,a)a 100 # 局部变量print(局部变量a,a)f() print(全局变量a,a)# 输出 函数中的全局变量 10 局部变量a 100 全局变量a 100最后需要注意的是可变的数据类型如list、set、dict他们的元素是可以在函数中修改的。 四、运算符与表达式 1. 算术运算符 运算符描述实例加101020-减10-20-10*乘10*20200/除10/200.5//取整除返回商的整数部分 9//2 4%取余数9 % 2 1**幂2**3 8 1.1. 算术运算符的优先级首先括弧优先级最高 幂运算 乘除 加减 ** * / % // - 2. 赋值运算符 运算符描述实例简单的赋值c ab加法赋值ca 等效于 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 3. 比较关系运算符 运算符描述比较两个值是否相等!比较两个值是否不相等比较左边值是不是大于右边值比较左边值是否小于右边值比较左边值是否大于等于右边值比较左边值是否小于等于右边值 4. 逻辑运算符 运算符表达式描述andx and yx 和 y 是否都是 True有一个False 则返回Falseorx or yx 和 y 有一个 True 则为 True 都为 False 则为 Falsenotnot xx 为 True 则返回False 如果为 False 则返回 True 5. 位运算符 运算符描述实例功能按位与运算符57参与运算的两个值如果两个相应位都为1则该位的结果为1否则为0|按位或运算符5|7参与运算的两个值如果两个相应位有一个为1时则该位的结果为1否则为0^按位异或运算符5^7参与运算的两个值如果两个相应位不同时则该位的结果为1则该位的结果为1否则为0-按位取反运算符-5对数据的每个二进制位运算进行取反操作把1变成0把0变成1左移动运算符92运算术的各二进制位全部向左移动若干位由符号右侧的数字指定移动的位数高位丢弃低位补0右移动运算符92运算术的各二进制位全部向右移动若干位由符号右侧的数字指定移动的位数低位丢弃高位补0 6. 成员运算符 右边必须是组合数据类型 运算符描述实例in如果在指定的序列中找到值则返回True否则返回False3 in (1,2,3) 返回Truenot in同 in 取 反3 not in (1,2,3) 返回 False 7. 身份运算符 运算符描述实例is判断两个标识符是否引用同一个对象是返回True否则返回Falsea 1 , b 1 , (a is b) 返回Trueis not同 is 取 反a 1 , b 2 , (a is not b) 返回True 8. 运算符的优先级 运算符描述**幂最高优先级~ -按位取反一元运算符正负号* / % //乘、除、取余数、取整除 -加、减 左移、右移按位与 比较运算符 !等于运算符 - * / // % **赋值运算符is is not身份运算符in not in成员运算符and or not逻辑运算符 第三章、基础语法 一、条件判断 1. 单分支判断 age 19 if age 18:print(可以进网吧)print(欢迎光临)2. 双分支判断 sex 男if sex 男:print(进男厕所) else:print(进女厕所)3. 多分支判断 score 87if score 100:print(A) elif score 90:print(A) elif score 80:print(B) elif score 60:print(C) else:print(D)4. match 匹配不同的值运行相应的逻辑相当于java中的switch sex 外星人 match sex:case 男:print(进男厕所)case 女:print(进女厕所)case _: # 表示匹配值print(你是变态吧)二、循环语句 1、for循环 for i in range(10):print(i)2、while循环 while n 10:print(n)n13. break 跳出距离最近的循环 for i in range(3):n 0print(i,i)while n 10:if n2:breakprint(n,n)n1 # 输出结果 i 0 n 0 n 1 i 1 n 0 n 1 i 2 n 0 n 14. 与 else 的结合 else 的下级代码没有通过break退出循环循环结束后会执行的代码 for i in range(5):if i 30:print(我不喜欢三)breakprint(i:,i) else:print(没有执行break) # 输出 i: 0 i: 1 i: 2 i: 3 i: 4 没有执行break5. continue 跳过本次最近循环执行下次循环 for i in range(5):if i 3:print(我不喜欢三)continueprint(i:,i) # 输出 i: 0 i: 1 i: 2 我不喜欢三 i: 46. pass 作用当语句要求不希望任何命令或者代码来执行时使用 说明pass语句表示一个空操作在执行时没有任何的响应pass的位置最终应该有代码来执行只不过暂时写不出来相当于占位符 使用可以使用在流程控制和循环语句中 for i in range(5):if i 3:print(我不喜欢三)breakprint(i:,i) else:pass # pass这里如果什么都不写原本是会编译报错的 三、异常 1、常见的异常类型 2、try-except语句 try:print(可能出现异常的代码) except:print(代码出现了异常)except 可以捕获到异常信息 try:print(可能出现异常的代码) except Exception as e:print(代码出现了异常,异常信息为,e)可以设置多个except 可以捕获指定的类型的异常信息 try:print(可能出现异常的代码) except ZeroDivisionError as e:print(出现了数字异常异常信息为,e) except Exception as e:print(代码出现了异常,异常信息为,e)3、与 else 语句结合当没有出现异常的时候执行 try:print(可能出现异常的代码) except ZeroDivisionError as e:print(出现了数字异常异常信息为,e) except Exception as e:print(代码出现了异常,异常信息为,e) else:print(未出现异常情况)3、 finally 表示一定会执行的代码 try:print(可能出现异常的代码) except ZeroDivisionError as e:print(出现了数字异常异常信息为,e) except Exception as e:print(代码出现了异常,异常信息为,e) else:print(未出现异常情况) finally:print(一定会执行的代码模块)4、 raise 手动抛出一个异常 try:print(可能出现异常的代码)raise TypeError(我手动抛出了一个类型异常) except ZeroDivisionError as e:print(出现了数字异常异常信息为,e) except Exception as e:print(代码出现了异常,异常信息为,e) else:print(未出现异常情况) finally:print(一定会执行的代码模块) # 输出 可能出现异常的代码 代码出现了异常,异常信息为 我手动抛出了一个类型异常 一定会执行的代码模块四、函数 1、函数的定义 使用关键字 def 确定函数名称、参数名称、参数个数、编写函数体用于实现函数功能的代码 def a():print(我是函数a)2、函数的调用 通过函数名称进行调用函数定义好的函数只表示这个函数疯涨了一段代码而已如果不主动调用函数函数是不会执行的 从上到下函数要先定义再调用 def a():print(我是函数a)a()3、函数的参数 形参就是函数定义时小括号里的参数是用来接收参数用的在函数内部作为变量使用。 实参函数调用的时候小括号里的参数是用来把数据传递到函数内部用的。 3.1、位置形参 def a(num1 , num2): # 形参print(我是函数a)print(参数1,num1)print(参数2,num2)a(1,e) # 实参 print(**10) a([1,2,3],2.4) # 实参# 输出 我是函数a 参数1 1 参数2 e ********** 我是函数a 参数1 [1, 2, 3] 参数2 2.43.2、默认形参缺省形参 在函数的定义的时候可以对形参设置一个默认值则这个参数在函数调用的时候可以不传递值取默认值也可以传入值则取传入的值 def defaultTest(num1,num2 3):return num1 * num2print(defaultTest(2)) # 2*36 print(defaultTest(2, 5)) # 2*510在设置多个形参的时候默认形参后边的形参也必须是默认形参 如果有多个缺省参数在调用函数传递实参的时候会按照顺序赋值的 如果要给指定的形参赋值则在函数调用的时候就需要指定实参的属性 def userTest(name,age20,gendernan):print(你好我叫%s我今年%d岁了我是一名%s生%(name,age,gender))userTest(ali,gender男)3.3、可变参数 参数的个数是若干个的可变参数必须是在形参的最后 3.1 tuple类型的可变参数 def tupleTotal(*args): # 接收的是一个 tupler 0for i in args:r ireturn rprint(tupleTotal(1, 2, 3, 4)) # 103.2 字典类型的可变参数 def dictTest(**kwargs): # 接收的是一个字典for k,v in kwargs.items():print(k,的值为,v)dictTest(**{name:李宁,age:23}) #调用的时候也要加上 ** # 输出 name 的值为 李宁 age 的值为 233.4、参数控制 3.4.1 关键字控制 声明函数时如果想要控制一些参数必须用关键字传入则可以使用 * 控制 如果单独出现星号 *则星号 * 后的参数必须用关键字传入 def f(a,b,*,c,d):return abcdprint(f(1, 2, 3, 4)) # 会报错 TypeError# 正确写法 print(f(1, 2, c3, d4)) 或者 print(f(1, 2, d3, c4))3.4.2 强制位置控制 / 用来指明函数形参必须使用指定位置参数表示斜杠前的参数不能使用关键字参数的形式必须根据位置传参。 在没有控制之前 def f(a,b,c,d):return abcdprint(f(b1, c2, d3, a4))控制之后 def f(a,b,/,c,d):return abcdprint(f(b1, c2, d3, a4)) # 报错 TypeError前俩个 a 和 b 必须不能使用关键字参数必须通过位置传参print(f(1, 2, d3, c4)) 或者 print(f(1, 2, 3, 4))4、函数的返回值 返回值是函数完成工作之后最后给调用者的一个结果使用 return 关键字调用者可以使用变量来接收函数的返回结果 def mysum(a,b):return abresult mysum(5,2) print(result) # 75、匿名函数 lambda函数是一种快速定义单行的最小函数可以用在任何需要函数的地方让代码更加简洁 lambda函数的写法是 lambda 参数: 函数逻辑 lambda的语法 lambda arguments: expressionlambda是 Python 的关键字用于定义 lambda 函数。arguments 是参数列表可以包含零个或多个参数中间用逗号隔开但必须在冒号(:)前指定。expression 是一个表达式用于计算并返回函数的结果。 # 1、无参 lambda 函数 f1 lambda : 无参lambda函数 print(f1()) # 无参lambda函数# 2、有参 lambda 函数 f2 lambda a : a 我是有参lambda函数 print(f2(你好)) # 你好我是有参lambda函数# 3、多参 lambda 函数 f3 lambda a, b, c: a**bc print(f3(2, 3, 4)) # 12lambda 函数通常与内置函数如 map()、filter() 和 reduce() 一起使用以便在集合上执行操作。 map()映射函数第一个入参是一个函数第二个参数是一个变量返回值为变量经过函数后的map对象 # 将列表中的元素乘以三次幂 m map(lambda x:x**3,[1,2,3,4,5]) print(list(m)) # [1, 8, 27, 64, 125]filter():过滤函数第一个入参是一个判断函数第二个入参是变量返回值为通过过滤条件的filter对象 # 使用 lambda 函数与 filter() 一起筛选偶数 numbers [1, 2, 3, 4, 5, 6, 7, 8] even_numbers filter(lambda x: x % 2 0, numbers) print(list(even_numbers)) # 输出[2, 4, 6, 8]下面是一个使用 reduce() 和 lambda 表达式演示如何计算一个序列的累积乘积 from functools import reducenumbers [1, 2, 3, 4, 5]# 使用 reduce() 和 lambda 函数计算乘积 product reduce(lambda x, y: x * y, numbers)print(product) # 输出120 # 1 * 2 * 3 * 4 * 5 120 的结果。6、闭包 定义 ①两个函数的嵌套 ②内层函数使用外层函数的入参(内层函数对外层函数有一个非全局变量的引用) ③外层函数返回内层函数本身 格式: def wai(a):def nei(b):print(外层函数入参:,a)print(内层函数入参:,b)return abreturn nein wai(100) # 调用外层函数返回一个内层函数 print(n) # function wai.locals.nei at 0x0000023CAECB8900 nr n(2) # 外层函数入参: 100# 内层函数入参: 2 print(nr) # 102 闭包如果想要修改外层函数的参数可以使用关键字 nonlocal表示定义此变量使用的是本地变量 ################# 没有使用 nonlocal 之前 ################### def wai():n 100 # n 为非全局变量def nei(b):n 200return nbreturn nei n wai() print(n(2)) # 202################# 使用 nonlocal 之后 ###################def wai():n 100 # n 为非全局变量def nei(b):nonlocal nn 200return nbreturn nein wai() print(n(2))7、装饰器相当于Java的代理模式 装饰器是一种函数它接受一个函数作为参数并返回一个新的函数或修改原来的函数。 装饰器的语法使用 decorator_name 来应用在函数或方法上。 Python 还提供了一些内置的装饰器比如 staticmethod 和 classmethod用于定义静态方法和类方法。 装饰器的应用场景 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。性能分析: 可以使用装饰器来测量函数的执行时间。权限控制: 装饰器可用于限制对某些函数的访问权限。缓存: 装饰器可用于实现函数结果的缓存以提高性能。 7.1、使用装饰器 装饰器通过 符号应用在函数定义之前例如 time_logger def target_function():pass 等同于 def target_function():pass target_function time_logger(target_function)7.2、自定义函数装饰器 # 自定义一个装饰器 decorator 形参是一个方法 # 在装饰器中定义一个方法用于接收被装饰的函数的入参返回被装饰的函数的返参在调用目标函数的前后可以对被装饰的函数进行增强 def decorator(func):def wrapper(*args, **kwargs):print(函数的入参为,*args,**kwargs)result func(*args, **kwargs)print(函数的原返回值为:,result)return str(result).upper() # 把函数的返回值转换成大写return wrapper# 使用 decorator 标在目标函数的头上表示装饰这个函数 decorator def greet(name):return fHello, {name}!print(函数最终的返回值,greet(Alice)) print(**50) print(函数最终的返回值,greet({name:miya,age:15}))# 输出函数的入参为 Alice 函数的原返回值为: Hello, Alice! 函数最终的返回值 HELLO, ALICE! ************************************************** 函数的入参为 {name: miya, age: 15} 函数的原返回值为: Hello, {name: miya, age: 15}! 函数最终的返回值 HELLO, {NAME: MIYA, AGE: 15}!7.3、还可以自定义带有参数的装饰器 # 创建一个修饰器传入一个函数名记录指定函数名的入参和返参 def repeat(name):def decorator(func):def wrapper(*args, **kwargs):print(函数:,name,的入参为,*args,**kwargs)result func(*args, **kwargs)print(函数:,name,的原返回值为:,result)return name_resultreturn wrapperreturn decorator# 使用 decorator 标在目标函数的头上表示装饰这个函数 repeat(greet) def greet(name):return fHello, {name}!print(函数最终的返回值,greet(Alice)) print(**50) print(函数最终的返回值,greet({name:miya,age:15}))# 输出 函数: greet 的入参为 Alice 函数: greet 的原返回值为: Hello, Alice! 函数最终的返回值 greet_Hello, Alice! ************************************************** 函数: greet 的入参为 {name: miya, age: 15} 函数: greet 的原返回值为: Hello, {name: miya, age: 15}! 函数最终的返回值 greet_Hello, {name: miya, age: 15}!7.4、类装饰器 除了函数装饰器Python 还支持类装饰器。类装饰器是包含 call 方法的类它接受一个函数作为参数并返回一个新的函数。 class DecoratorClass:def __init__(self, func):self.func funcdef __call__(self, *args, **kwargs):print(在调用原始函数之前/之后执行的代码)result self.func(*args, **kwargs)print(在调用原始函数之后执行的代码)return resultDecoratorClass def my_function(a,b,c):print(执行函数my_function,(abc))return abcmy_function(1, 2, 3)# 输出在调用原始函数之前/之后执行的代码 执行函数my_function 6 在调用原始函数之后执行的代码 五、模块 简单理解就是一个.py文件就是一个模块在一个文件中可以通过import引入另外一个模块使用被引入的模块的变量和方法 1、模块的简单使用 创建一个my_module的python文件 i 100 s my_module的字符串 l list(my_module的列表)def hello():print(你好我是hello方法)return hellodef add(a,b):return ab在另一个python中使用 import 引入这个模块 import my_moduleprint(my_module.s) # my_module的字符串imy_module.iprint(my_module.l) # [m, y, _, m, o, d, u, l, e, 的, 列, 表]print(my_module.hello()) # 你好我是hello方法# helloprint(my_module.add(i, len(my_module.l))) # 112 2、import 语句 在一个模块中引入另外一个模块语法如下 import module1[, module2[,... moduleN]当我们使用 import 语句的时候Python 解释器会从搜素路径列表中依次去寻找所引入的模块可以通过sys.path查看路径目录列表 import sys print(sys.path) # 输出 [D:\\python\\PycharmProjects\\pythonProject001, D:\\python\\PycharmProjects\\pythonProject001, D:\\python\\PycharmProjects\\pythonProject001\\.venv\\Scripts\\python313.zip, C:\\Users\\14911\\AppData\\Local\\Programs\\Python\\Python313\\DLLs, C:\\Users\\14911\\AppData\\Local\\Programs\\Python\\Python313\\Lib, C:\\Users\\14911\\AppData\\Local\\Programs\\Python\\Python313, D:\\python\\PycharmProjects\\pythonProject001\\.venv, D:\\python\\PycharmProjects\\pythonProject001\\.venv\\Lib\\site-packages] 如果不存在则编译报错 3、from … import 语句 Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中语法如下 from modname import *这样别导入的变量或者函数可以直接在这个模块中使用而不是像只导入模块那种通过模块.函数的形式调用 from my_module import l,hellol.append(我可以直接使用)print(l) # [m, y, _, m, o, d, u, l, e, 的, 列, 表, 我可以直接使用]hello() # 你好我是hello方法4、__name__属性 一个模块被另一个程序第一次引入时其主程序将运行。如果我们想在模块被引入时模块中的某一程序块不执行我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。 #!/usr/bin/python3 # Filename: using_name.pyif __name__ __main__:print(程序自身在运行) else:print(我来自另一模块) 运行输出 程序自身在运行导入到另一个模块中使用 import using_nameprint(using_name.l)说明 每个模块都有一个__name__属性当其值是’main’时表明该模块自身在运行否则是被引入。 5、包 包是一种管理 Python 模块命名空间的形式采用点模块名称比如一个模块的名称是 A.B 那么他表示一个包 A中的子模块 B 。 在python3之前目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包。 python3之后没有 __init__.py文件的目录也为被认定为包可以引入模块但是建议依然遵循之前的规则。 导入方式可以分为两种 单独使用 import格式 import 包.包.模块# 使用 包.包.模块.变量 包.包.模块.函数这样的弊端就是在使用的时候要使用带包名的前缀 配合使用 from…import格式 from 包.包 import 模块# 使用 模块.变量 模块.函数使用的时候不需要那些冗长的前缀 from bao02 import my_module02print(my_module02.add(1, 2)) # 3 print(my_module02.l) # [m, y, _, m, o, d, u, l, e, 的, 列, 表]6、__init__.py 的 __all__ 当我们使用 from sound.effects import * 的时候会导入这个包下的所有模块但是我们如果想指定这个时候导入哪些模块的时候可以在这个包的__init__.py中定义__all__列表属性 __all__ [my_module01,my_module02]注意不要带 .py from bao01 import *print(my_module02.add(my_module01.i, len(my_module01.l))) # 112在上边的例子中在包bao01的 __init__.py 文件中设置 __all__ [my_module01,my_module02] 则在使用 from bao01 import * 引入 bao01 的时候只会引入 my_module01 和 my_module02 这两个模块不会引入 my_module 模块。 需要注意的是 my_module 模块并不是不会被引入其他模块了可以通过具体的路径来引入 from bao01 import my_modulemy_module.hello() # 你好我是hello方法六、迭代器与生成器 1、可迭代对象 1.1、内置可迭代对象 像 字符串集合列表元组字典这样能够通过 for in 去迭代的对象就是被称为迭代对象上边这些是python中天然的可迭代对象那么我们自己创建的对象如果也像成为可迭代对象的话需要重写 __iter()__魔术函数并且这个函数返回一个迭代器对象。 可迭代对象本质 其实都是collections.abc(容器数据类型)模块里的 Iterable (可迭代对象)类创建出来的实例可以通过 isinstance(对象,Iterable)查看对象是否是可迭代对象 from collections.abc import Iterablel [1,2,3] print(isinstance(l,Iterable)) # Trueprint(isinstance((2,3,4),Iterable)) # Trueprint(isinstance(abcd,Iterable)) # True1.2、自定义可迭代对象 知道了可迭代对象的特性之后除了系统自带的可迭代对象外我们自己创建的对象如果也想有可迭代的属性则需要通过重写 __iter()__ 魔术函数并必须返回一个迭代器对象。 class A:def __iter__(self):self.n 1return selfprint(isinstance(A(),Iterable)) # True2、迭代器 迭代器对象从集合的第一个元素开始访问直到所有的元素被访问完结束。迭代器只能往前不会后退。 迭代器有两个基本的方法iter() 和 next()。 iter(可迭代对象)创建一个迭代器对象 next(迭代器对象)从第一个元素开始每执行一次获取下一个元素list[1,2,3,4] # 可迭代对象 it iter(list) # 创建迭代器对象 print (next(it)) # 输出迭代器的下一个元素 1 print (next(it)) # 2迭代器对象也可以使用 for 循环进行遍历 #!/usr/bin/python3list[1,2,3,4] it iter(list) # 创建迭代器对象 for x in it:print (x, end )#输出 1 2 3 4 迭代器对象所有元素取出之后继续执行 next() 时会报 StopIteration 异常根据这个特性我们也可以使用 while 循环遍历元素 list[1,2,3,4] it iter(list) # 创建迭代器对象while True:try:print(next(it))except StopIteration:print(取完了)break自定义迭代器对象 自定义迭代器对象需要有两个魔法函数 __iter__ 和 __next__ iter函数返回迭代器对象next函数返回迭代器对象的下一个值需要注意的是规范需要设置取值范围当超过指定范围的时候需要抛出 StopIteration 异常 # 创建一个初始值为 1 依次递增 1 的迭代器对象最大取到 5 class A:def __iter__(self):self.num 1return selfdef __next__(self):num self.numif num 5: # 当超过 5 的时候抛出 StopIteration 异常raise StopIterationself.num1return numa A() it iter(a) # 创建一个迭代器对象for i in iter(a):print(i,end ) # 输出 1 2 3 4 5 3、生成器 在Python中yield关键字用于从一个函数中返回一个生成器generator。生成器是一种迭代器它允许你按需生成值而不是一次性将所有值加载到内存中。这对于处理大量数据或需要惰性求值的场景非常有用。 以下是yield的一些关键特性和用法 3.1、基本用法 使用yield的函数称为生成器函数。调用生成器函数时会返回一个生成器对象而不是立即执行函数体。生成器对象可以使用next()函数或在循环中迭代来逐个获取值。 def simple_generator():yield 1yield 2yield 3gen simple_generator()print(next(gen)) # 输出: 1 print(next(gen)) # 输出: 2 print(next(gen)) # 输出: 33.2、生成器推导式 同列表推导式一样把列表推导式中的 [ 换成 ( 就可以创建生成器推导式 # 列表推导式list [i*2 for i in range(5)]print(type(list)) # class list print(list) # [0, 2, 4, 6, 8]# 生成器推导式it (i*2 for i in range(5))print(type(it)) # class generator print(it) # generator object genexpr at 0x000001CD288FA9B0 for i in it:print(i,end ) # 0 2 4 6 83.3、在循环中使用 yield通常在循环中使用以按需生成一系列值。 def range_generator(start, end):current startwhile current end:yield currentcurrent 1for num in range_generator(0, 5):print(num) # 输出: 0 1 2 3 43.4、保存函数状态 当生成器函数产生一个值后它的执行状态会被保存直到下一次调用next()。这意味着函数中的局部变量和控制流状态在后续调用时仍然存在。 def counter():count 0while True:count 1yield countgen counter()print(next(gen)) # 输出: 1 print(next(gen)) # 输出: 2 print(next(gen)) # 输出: 33.5、结合send()方法有入参 除了next()生成器对象还支持send()方法。send(value)会向生成器发送一个值并在生成器函数上次暂停的地方恢复执行同时将发送的值赋给yield表达式的结果。 def echo_generator():while True:received yieldprint(fEcho: {received})gen echo_generator() next(gen) # 启动生成器 gen.send(Hello) # 输出: Echo: Hello gen.send(World) # 输出: Echo: World注意在第一次使用生成器之前必须先调用next(gen)来启动生成器否则第一次调用send()会抛出异常。 3.6、使用yield from yield from是Python 3.3引入的语法用于在生成器函数中委托另一个生成器。 def sub_generator():yield ayield bdef main_generator():yield xyield from sub_generator()yield yfor item in main_generator():print(item) # 输出: x a b y总结 yield关键字在Python中提供了一种强大而灵活的机制来创建迭代器它允许函数在多个调用之间保持状态并且按需生成值。这使得处理大量数据或需要惰性计算的场景变得更加高效和简洁。 第四章、面向对象 一、类 在python中通过 class 来定义一个类 1、类的创建 定义一个类有三种格式写法不同其他都相同 # 第一种格式 class A(object):name Adef f(self):print(name:,self.name)return self.name# 第二种格式 class B():name Bdef f(self):print(name:,self.name)return self.name# 第三种格式 class C:name cdef f(self):print(name:,self.name)return self.name2、类的属性——变量 在类中可以设置成员变量 class A(object):name Aage 18gender 男print(A.name) # A以上都是普通的变量通过类名或者对象可以直接获取如果不希望外部可以直接获取可以设置成私有变量通过 __变量名 设置 class A(object):name Aage 18gender 男__weight 120print(A.name) # A print(A.__weight) # AttributeError 异常可以通过类内部的函数获取私有变量 class A(object):__weight 120def getweight(self):return self.__weight a A() print(a.getweight()) # 120 3、类的属性——类函数 类的成员属性函数可以分两种 一种是通过类直接调用的 一种是需要通过类生成的对象才可以调用的 # 通过类直接调用的方法 class A(object):def f():print(函数f) print(A.f()) # 通过类名直接调用一般我们设计一个类的方法的时候规范是都要使用 classmethod 注解的 classmethod注解的函数表示为类函数此函数的规范最少有一个参数cls表示类本身cls可以掉类的属性 class A(object):name Aclassmethoddef f(cls):print(函数f)print(cls.name)print(A.f()) # 函数f# A4、类的属性——静态函数 在类内部的方法上使用 staticmethod 注解可以表示这个函数是静态函数静态函数既不需要传对象参数也不需要传类参数selfcls 静态函数的作用主要是可以设置类的属性 class A:__name a # 私有属性staticmethod # 静态函数def sm(r):print(我是静态函数smname为:,A.__name,入参为,r) # 类内部可以获取私有属性A.__name name新值 # 可以修改类的私有属性return 我是A.__name静态函数的返参r # 可以把私有属性返回print(A.sm(类直接调用)) # 我是静态函数smname为: a 入参为 类直接调用# 我是name新值静态函数的返参类直接调用print(A().sm(通过对象调用)) # 我是静态函数smname为: name新值 入参为 通过对象调用# 我是name新值静态函数的返参通过对象调用5、类的属性——对象函数 对象函数规定第一个入参为 self 表示对象本身 class A(object):__name Adef f(self):print(self.__name)print(f函数)return f函数的返参a A() print(a.f()) # A# f函数# f函数的返参 print(A.f()) # TypeError类的函数同样也可以设置成私有函数 class A:def __f(self):print(我是一个私有函数)return 私有函数的返参def f(self):print(我是对象函数f)return self.__f()a A()fr a.f() # 我是对象函数f# 我是一个私有函数 print(fr) # 私有函数的返参a.__f() # AttributeError6、类中函数的调用权限 函数特性类可直接调用对象可直接调用普通函数没有装饰器和入参是否静态函数staticmethod 装饰器没有入参是是类函数classmethod装饰器入参(cls)是是对象函数没有装饰器,入参(self)否是 class A:def f1():print(普通函数)staticmethoddef f2():print(静态函数)classmethoddef f3(cls):print(类函数)def f4(self):print(对象函数)A.f1() # 类调普通函数 A.f2() # 类调静态函数 A.f3() # 类调类函数 A.f4() # 类调对象函数 报错a A() a.f1() # 对象调普通函数 报错 a.f2() # 对象调静态函数 a.f3() # 对象调类函数 a.f4() # 对象调对象函数ps:类调用对象函数可以通过传入一个对象来进行调用 class A:def f(self):print(对象函数)a A() # 实例化一个对象 A.f(a) # 通过类调用对象函数传如一个对象 对象函数7、类的属性的增删改查 通过 __dict__ 可以查看类的所有属性 class A:name Adef f(self):print(f函数)return f函数的返参# 查看 print(A.__dict__) # 查看所有属性 # {__module__: __main__, __firstlineno__: 23, name: A, f: function A.f at 0x0000028D37F48900, __static_attributes__: (), __dict__: attribute __dict__ of A objects, __weakref__: attribute __weakref__ of A objects, __doc__: None}print(A.__dict__[name]) # 查看指定属性# 新增 A.age 20 print(A.age) # 20# 修改 A.name AA print(A.name) # AA# 删除 del A.name print(A.name) # AttributeError设置属性总结一点就是如果本身有这个属性则是修改属性的值如果没有则是新增一个属性 重点给类设置完属性之后生成的对象是有这个属性值的 class A:name Adef fun(self):print(fun函数,age:,self.age) # 打印对象的 age 值return fun函数返参A.age 20 # 给 A 类设置属性 A.f fun # 给 A 类设置对象函数 a A() # 通过类 A 生成对象 aprint(a.age) # 通过对象 a 调用 age 属性 ( 20 af a.f() # 通过对象 a 调用 f 函数 ( fun函数,age: 20 print(af) # 打印 f 函数返参 ( fun函数返参二、对象 通过类创建对象类相当于是一个模板通过这个模板可以创建多个对象 1、对象的创建 对象创建的格式 对象 类() 2、对象的使用 通过对象可以调用类的非私有变量和方法类方法和对象方法都可以调用 class A:name Adef f(self): # 对象函数print(f函数)return f函数的返参classmethoddef f2(cls): # 类函数print(f2函数)return f2函数的返参a A() print(a.name) # 调用变量 A fr a.f() # 调用对象函数 f函数 print(fr) # f函数的返参 f2r a.f2() # 调用对象函数 f2函数 print(f2r) # f2函数的返参注意 对象函数只能通过对象调用 类函数可以通过类名调用也可以通过对象调用 3、对象的属性和函数 对象除了通过类的属性生成自己的属性外还可以通过自己手动设置属性和函数 通过 __dict__ 可以查看对象的属性不包含类的属性 class A:age 20def f(self):print(f函数,self.name)return f函数的返参def f2(n):print(我是手动添加的函数,n)return 手动添加的函数的返参aA() # 查看对象 a 的所有属性 手动设置的属性 print(a.__dict__) # {}# 手动添加属性 a.name 我是手动添加的属性 print(a.name) # 打印手动添加的属性 ( 我是手动添加的属性 a.f() # 执行函数这个函数打印了新添加的属性 ( f函数 我是手动添加的属性# 手动添加函数 a.af2 f2 # 给对象添加函数 af2r a.af2(aaa) # 调用手动添加的函数 我是手动添加的函数 aaa print(af2r) # 手动添加的函数的返参# 查看对象 a 的所有属性 手动设置的属性 print(a.__dict__) # {name: 我是手动添加的属性, af2: function f2 at 0x000001CE79FA3C40}三、基础魔术函数 在python中通过 __函数名__ 来表示魔法函数这些函数在类和对象中起着非常关键的作用就行表演魔术一样所以被称为魔术函数通过 dir() 函数可以查看都有哪些魔术函数 print(dir(object)) # 输出 [__class__, __delattr__, __dir__, __doc__, __eq__, __format__, __ge__, __getattribute__, __getstate__, __gt__, __hash__, __init__, __init_subclass__, __le__, __lt__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __str__, __subclasshook__] 1、__init__(self) 入参对象 返参无 在类实例化创建对象的时候调用创建一个调用一次入参是一个对象一般用于给对象的属性初始化赋值 class A:def __init__(self):self.name lgh # 给对象添加属性self.age 20print(init函数调用)a A() # init函数调用 print(a.name,a.age) # lgh 20init方法可以增加形参在实例化对象的时候添加对应的实参来动态的给对象初始化属性 class A:def __init__(self,name,age): # 增加形参self.name name # 给对象添加属性self.age ageprint(init函数调用)a A(张三,23) # 创建对象的时候必须传入对应的参数 print(a.name,a.age) # 张三 232、__del__(self) 入参对象 返参无 在对象销毁的时候调用销毁一个对象调用一次 class A:def __init__(self,name):self.name name print(对象 %s 创建了 % self.name)def __del__(self):print(对象 %s 销毁了 % self.name)a1 A(张三) print(**20) a2 A(李四)# 输出对象 张三 创建了 ******************** 对象 李四 创建了 对象 张三 销毁了 对象 李四 销毁了对象在程序运行结束直接会自动销毁也可以通过 del 在程序中手动销毁对象 class A:def __init__(self,name):self.name nameprint(对象 %s 创建了 % self.name)def __del__(self):print(对象 %s 销毁了 % self.name)a1 A(张三) del a1 # 手动删除 a1 对象 print(**20) a2 A(李四)# 输出对象 张三 创建了 对象 张三 销毁了 ******************** 对象 李四 创建了 对象 李四 销毁了 3、__new__(cls, *args, **kwargs) 入参类本身和类的所有入参 返参必须要有返回一个对象 在类实例化对象的过程是先执行 new 函数再执行 init 函数init 函数的作用是给对象初始化赋值 new 函数的作用是创建一个对象并且返回如果new函数没有返回一个对象则对象创建失败也就不会调用 init 函数 class A:def __init__(self,name):self.name nameprint(对象 %s 创建了 % self.name)def __new__(cls, *args, **kwargs):print(new函数调用)# new函数没有返回值则之后调用 new 函数不会调用 init 函数 a1 A(张三)# 输出 new函数调用正确的写法 class A(object):def __init__(self,name):self.name nameprint(对象 %s 创建了 % self.name)def __new__(cls, *args, **kwargs):print(new函数调用)# 所有的类都是object类的子类object的 new 函数也得返回一个对象所以把这个对象返回就可以了意思就是通过 object 创建一个对象return object.__new__(cls)# 也可以使用 supper().__new__(cls) 意义和上边是一样得写法不同而已#return super().__new__(cls)a1 A(张三)# 输出 new函数调用 对象 张三 创建了3、__repr__(self) 和 _srt_(self) 入参对象 返参必须要有对象的介绍 相当于java中的 toString 方法再打印对象的时候显示的信息两个方法都存在的时候优先显示 str 函数的返参 当没有定义这两个函数的时候打印对象的信息显示的是内存空间的地址 class A(object):def __init__(self,name):self.name nameprint(对象 %s 创建了 % self.name)a A(张三) print(a)# 输出 对象 张三 创建了 __main__.A object at 0x000002629E9C7CB0 定义 __repr__(self) 函数 class A(object):def __init__(self,name):self.name nameprint(对象 %s 创建了 % self.name)def __repr__(self):return repr函数返回我是对象self.namea A(张三) print(a)# 输出 对象 张三 创建了 repr函数返回我是对象张三当两个函数都定义则显示 str 返回的内容 str 优先级高于 repr class A(object):def __init__(self,name):self.name nameprint(对象 %s 创建了 % self.name)def __repr__(self):return repr函数返回我是对象self.namedef __str__(self):return str函数返回我是对象self.namea A(张三) print(a)# 输出对象 张三 创建了 str函数返回我是对象张三 4、__doc__ 表示类的注释信息 通过类名直接调用表示类的注释信息注意这个注释信息的格式有一定的要求 必须是在所有属性前边必须是多行注释三个单引号或双引号必须和属性缩进对齐 class A:类的注释信息注意必须是在所有属性前边 name adef f(self):print(f函数)print(A.__doc__) # 类的注释信息注意必须是在所有属性前边 5、__doc__ 表示当前操作的对象在哪个模块 通过对象调用表示当前对象来自哪个模块 from 类 import Aa A()print(a.__module__) # 类6、__doc__ 表示当前操作的对象的类是哪个 通过对象调用显示该对象的类信息 from 类 import Aa A()print(a.__class__) # class 类.A7、__call__(self, *args, **kwargs)表示通过 “对象”来调用的方法 对象的init方法是在 “类名” 这种形式调用而 call函数可以通过 “对象” 这种形式调用 并且这个函数是可以传参的 class A:def __init__(self):print(__init__函数执行)def __call__(self, *args, **kwargs):print(__call__函数执行)print(args)a A() # __init__函数执行a(1,2,3) # __call__函数执行# (1, 2, 3)8、__dict__查看类或者对象的所有属性 对象和类都可以调用 class A:name adef f(self):print(f函数)print(A.__dict__) # {__module__: __main__, __firstlineno__: 135, name: a, f: function A.f at 0x000002AD389B8900, __static_attributes__: (), __dict__: attribute __dict__ of A objects, __weakref__: attribute __weakref__ of A objects, __doc__: None}a A() print(a.__dict__) # {} a.age 18 print(a.__dict__) # {age: 18}print(dir(A)) # [__class__, __delattr__, __dict__, __dir__, __doc__, __eq__, __firstlineno__, __format__, __ge__, __getattribute__, __getstate__, __gt__, __hash__, __init__, __init_subclass__, __le__, __lt__, __module__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __static_attributes__, __str__, __subclasshook__, __weakref__, f, name] print(dir(a)) # [__class__, __delattr__, __dict__, __dir__, __doc__, __eq__, __firstlineno__, __format__, __ge__, __getattribute__, __getstate__, __gt__, __hash__, __init__, __init_subclass__, __le__, __lt__, __module__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __static_attributes__, __str__, __subclasshook__, __weakref__, age, f, name] 同样是查看类和对象的属性dir和 __dict__ 有什么区别呢主要有以下区别: dir() 是一个基本函数通过传参调用__dict__是魔术函数通过对象或者类直接调用dir()返回的是一个列表__dict__返回的是一个字典dir()传入对象的时候返回信息会包括生成这个对象的类的属性__dict__不会包括类的属性只会显示对象的属性dir()所有对象都可以调用__dict__并不是所有对象都拥有的属性许多内建类型都没有 __dict__ 属性例如 list 9、__getitem__ __setitem__ __delitem__ 让对象有字典的功能 __getitem__(self, item)通过“对像[属性key]” 语法获取值的时候调用需要有返回值返回属性key对应的 value 值__setitem__(self, key, value)通过 “对像[属性key]” value 语法给属性设置值的时候调用无返回值__delitem__(self, key)通过 “del 对像[属性key]”语法删除属性key的时候调用无返回值 注意这三个魔术函数本身不能让对象具有字典的功能需要在这三个函数中自己写逻辑 class A:def __init__(self):self.data {} # 创建一个字典用来放数据def __getitem__(self, item):print(__getitem__函数执行, item)return self.data.get(item)def __setitem__(self, key, value):print(__setitem__函数执行, key, value)self.data[key] valuedef __delitem__(self, key):print(__delitem__函数执行, key)del self.data[key]a A()a[name] 张三 # 给对象设置属性 __setitem__函数执行 name 张三 name a[name] # 获取对象属性 __getitem__函数执行 name print(name) # 张三del a[name] # __delitem__函数执行 nameprint(a[name]) # __getitem__函数执行 name# None四、封装 python和java一样是一个面向对象的一个编程它也有封装的一个特性将复杂的流程信息包装起来内部处理让使用者通过简单的调用就能实现。 1、私有属性 在类中可以通过使用 __属性名 或者 __函数名 来定义私有属性私有属性不能被外部调用只能在类中被调用 1.1 、普通属性 class A:name aadef f(self):return f函数self.namea A() print(a.name) # aa print(a.f()) # f函数aa1.2 、假的私有属性 通过 _属性单下划线来定义它只是一种标识这个属性是私有属性但是对象其实是可以再外部调用的 class A:_name aadef _f(self):return f函数self._namea A() print(a._name) # aa print(a._f()) # f函数aa1.3 、私有属性 class A:__name aadef __f(self):return f函数self.__name # 再函数内部可以获取到私有属性 __namedef f2(self):return self.__f() # 在 函数 f2 内部可以调用私有函数 __fa A() print(a.f2()) # f2函数aa print(a.__name) # AttributeError print(a.__f()) # AttributeError2、获取私有属性 由于私有属性不能在外部直接获取但是可以在类内部获取所有可以设置共有函数返回私有属性的方式来获取私有属性 class A:__name aadef get_name(self):return self.__name # 在函数内部可以获取到私有属性 __name a A() print(a.get_name()) # 通过get_name函数__name的值 ( aa3、修改私有属性 和获取私有属性一样通过共有函数在类内部修改私有属性 class A:__name aadef set_name(self,new_name):self.__name new_name # 在函数内部修改私有属性 __name 的值 def get_name(self):return self.__namea A() a.set_name(bb) print(a.get_name()) # 通过get_name函数__name的值 ( bb五、继承 1、如何继承 同java一样python也有继承的特性其实所有的类都继承于 object 在前边的类的创建哪里的第一种定义就展现出了python继承的特性 class A(object):name aadef f1():print(aa)return name同样我们也可以继承自己定义的类 class A(object):name aadef f1(self):print(aa)return name# B 类 继承 A 类 class B(A):age 20 2、继承之后的使用 在类 B 继承类 A 之后B 实例化的对象就也有了类 A 的属性 class A(object):name aadef f1(self):print(aaaa)class B(A):age 20b B() print(b.name) # aa b.f1() # aaaa print(b.age) # 203、继承之重写和扩展 如果子类的方法或者属性和父类名字相同则为重写如果不同则为扩展 比如 B类继承于A类A的属性 name “a” ,B类的属性 name“b” 则生成的bB()对象的name是子类的b class A:name aaclass B(A):name bbb B()print(b.name) # bbpython继承的函数中是要是函数名相同则为重写只通过函数名不通过参数区分如果参数不同则根据子类的参数为准这点和 Java 有所区别 class A:def f1(self):print(aaaa)class B(A):# 方法名和父类中方法名相同但参数不同的情况下def f1(self,name):print(bbbb,name)b B()b.f1(33) # bbbb333 b.f1() # TypeError 报异常认为缺少入参 4、子类调用父类的属性 在子类的函数中可以需要父类的属性这个时候就需要通过 super() 关键字 class A:name aadef f1(self):print(aaaa)class B(A):def __init__(self):self.a_name super().name # 子类的 a_name 取 父类 name 属性的的值def f2(self):super().f1() # 子类调用 父类的 f1() 函数print(bbb)b B() print(b.a_name) # aa b.f2() # aaaa# bbb需要注意的是子类中访问不了父类的私有属性如果想要访问也是需要通过父类的共有方法如果父类提供共有方法的前提下 class A:__name 我是私有变量namedef __f1(self):print(我是私有方法f1)class B(A):def get_name(self):print(super().__name) # AttributeErrordef get_f1(self):super().__f1() # AttributeErrorb B() b.get_name() b.get_f1() 5、多继承 python中通过在定义类的时候再括号中添加多个类实现多继承中间用 “,” 隔开属性的优先级从左到右依次降低 class A:name 我是A类的属性namedef f(self):print(我是A类的函数f)def ff(self):print(我是A类的函数ff)class B:name 我是B类的属性namedef f(self):print(我是B类的函数f)def ff(self):print(我是B类的函数ff)class C(A,B): # 多继承age 20def ff(self):print(我是C类的函数ff)c C() print(c.name) # 我是A类的属性name c.f() # 我是A类的函数f c.ff() # 我是C类的函数ff同样在子类中使用 super() 调用相同名字的属性的时候也是遵循从右往左的优先级顺序 class A:name 我是A类的属性namedef f(self):print(我是A类的函数f)class B:name 我是B类的属性namedef f(self):print(我是B类的函数f)class C(A,B):def __init__(self):self.super_name super().namedef getf(self):super().f()c C() c.getf() # 我是A类的函数f print(c.super_name) # 我是A类的属性name如果想要知道一个类的调用优先级可以使用 __mro__ 这个魔术函数来查看 格式为类名.__mro__ print(C.__mro__) #输出 (class __main__.C, class __main__.A, class __main__.B, class object)如果想调用指定父类的属性可以通过引用父类名直接调用 class A:name A类的namedef f(self):print(A类的函数f)class B:name B类的namedef f(self):print(B类的函数f)class C(A,B):def get_super_f(self):super().f()return super().namedef get_f(self):B.f(self) # 类直接调用对象方法需要把对象传入进去return B.namec C()sf c.get_super_f() # A类的函数f print(sf) # A类的namef c.get_f() # B类的函数f print(f) # B类的name6、继承的传递 继承是可以传递的子类继承父类父类也可以继承爷爷类然后子类也会有爷爷类的属性如果属性名相同则优先级是继承关系越近优先级越高 class A:name 我是爷爷类的namedef f(self):print(我是爷爷类的函数f)def d(self):print(我是爷爷类的函数d)class B(A): # B 继承 Adef d(self):print(我是父类的函数d)class C(B): # C 继承 Bpassc C()print(c.name) # 我是爷爷类的name c.f() # 我是爷爷类的函数f c.d() # 我是父类的函数dprint(C.__mro__) # (class __main__.C, class __main__.B, class __main__.A, class object) 六、单例模式 定义一个类只创建一个对象这样的类被称为单例模式 正常通过类可以创建N个对象如何控制类只能创建一个对象呢一共有四种方式可以定义一个单例模式的类 1、通过 __new__ 实现 主要实现方法在类中定义一个变量用来保存对象建议设置成私有属性在 new 方法中判断这个变量是否有值如果有值则直接返回如果没值则创建一个对象并且保存到这个变量中返回变量这样这个类就只会创建一个对象。 class A:__a None # 建议设置成私有属性def __new__(cls, *args, **kwargs):if cls.__a is None: # 如果变量为 nonecls.__a super().__new__(cls) # 创建对象 并赋值return cls.__adef __init__(self,name):self.name namea1 A(张三) a2 A(李四)# 同一个对象 print(id(a1)) # 1946691206320 print(id(a2)) # 1946691206320 # 创建的对象叫张三后边的李四使用的还是张三这个对象以先创建的为主 print(a1.name) print(a2.name)2、通过 classmethod 类方法实现 主要实现方法同 new 函数类似也是定义一个变量来保存类对象不同的是这次通过类方法来判断这个变量是否有值没值则创建对象赋值并返回这个变量。 注意点严格来说需要定义 __new__ 函数然后返回用于保存对象的变量这样可以避免通过 对象类() 的方式创建多个对象使这个类只能创建一个对象。 class A:__a Nonename def __new__(cls, *args, **kwargs):return cls.__aclassmethoddef get_obj(cls):if cls.__a is None: # 如果变量为 nonecls.__a super().__new__(cls) # 创建对象 并赋值return cls.__adef __init__(self):print(执行init)a1 A() print(a1) # Nonea2 A.get_obj() a3 A.get_obj()print(a1) # __main__.A object at 0x0000027573B67CB0 print(a2) # __main__.A object at 0x0000027573B67CB0 a2.name 张三 # 给a2对象的name赋值 print(a3.name) # 打印a3对象的name显示 ”张三“ 说明是同一个对象a4 A() # 由于 A 类的 __a 有值 new 函数返回有值的对象后 init 函数就执行了 执行init print(a4.name) # 由于是同一个对象所以依然显示张三 张三3、通过装饰器来实现 主要实现方法通过定义一个类装饰器这个装饰器中定义一个字典这个字典key是类value是类的对象通过判断这个key是否在这个字典中如果不在字典中则通过类创建一个对象生成一个键值对保存到字典中如果存在则不创建对象。最后通过类最为key返回这个字典中保存的value也就是对象数据这样保证一个类只会实例化一个对象。 def my_singleton(clas):__objs {} # 定义一个字典用于保存类和对象的键值对key 为类value 为类的对象通过判断key是否存在不存在则创建对象并赋值保存存在则不创建最后返回value值也就是对象def get_obj():if clas not in __objs:__objs[clas] clas() # 创建一个对象生成成一个键值对保存到 __objs 字典中return __objs[clas]return get_objmy_singleton class A:name Nonea1 A() a2 A()print(a1) # __main__.A object at 0x000001E15E477CB0 print(a2) # __main__.A object at 0x000001E15E477CB0 a1.name 张三 print(a2.name) #张三 3、通过元类来实现 hasattr(obj,name)用于判断对象是否包含对应的属性。obj对象name属性名包括变量和函数。获取不到私有属性 class A:name None__age Nonedef f1(self):passdef __f2(self):passclassmethoddef f3(cls):passstaticmethoddef f4():passa A() print(hasattr(a, name)) # True print(hasattr(a, __age)) # False print(hasattr(a, f1)) # True print(hasattr(a, __f2)) # False print(hasattr(a, f3)) # True print(hasattr(a, f4)) # True print(hasattr(a, abcde)) # False主要实现方法在 __new__ 函数中通过 hasattr() 函数判断类是否有指定对象属性如果没有则创建赋值如果有则不做处理最终返回这个对象属性的值 class A:def __new__(cls, *args, **kwargs):if not hasattr(cls,obj): # 注意不能是私有属性cls.obj super().__new__(cls)return cls.obja1 A() a2 A() print(a1) # __main__.A object at 0x000001BB8D537CB0 print(a2) # __main__.A object at 0x000001BB8D537CB0a1.name 张三 print(a2.name) # 张三同样使用类方法也可以实现 class A:classmethoddef get_obj(cls):if not hasattr(cls,obj):cls.obj super().__new__(cls)return cls.objdef __new__(cls, *args, **kwargs):if hasattr(cls,obj):return cls.obja1 A() print(a1) # None a2 A.get_obj() a3 A.get_obj() print(a1) # None print(a2) # __main__.A object at 0x0000024E6B267CB0 print(a3) # __main__.A object at 0x0000024E6B267CB0a4 A() print(a4) # __main__.A object at 0x0000024E6B267CB0第五章、文件读写 Python open() 方法用于打开一个文件并返回文件对象在对文件进行处理过程都需要使用到这个函数如果该文件无法被打开会抛出 OSError。 注意使用 open() 方法一定要保证关闭文件对象即调用 close() 方法。 open() 函数常用形式是接收两个参数文件名(file)和模式(mode)。 open(file, moder)完整的语法格式为 open(file, moder, buffering-1, encodingNone, errorsNone, newlineNone, closefdTrue, openerNone)参数说明: file: 必需文件路径相对或者绝对路径。 mode: 可选文件打开模式 buffering: 设置缓冲 encoding: 一般使用utf8 errors: 报错级别 newline: 区分换行符 closefd: 传入的file参数类型 opener: 设置自定义开启器开启器的返回值必须是一个打开的文件描述符。mode 参数有 模式描述t文本模式 (默认)。x写模式新建一个文件如果该文件已存在则会报错。b二进制模式。打开一个文件进行更新(可读可写)。U通用换行模式不推荐。r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。r打开一个文件用于读写。文件指针将会放在文件的开头。rb以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。w打开一个文件只用于写入。如果该文件已存在则打开文件并从开头开始编辑即原有内容会被删除。如果该文件不存在创建新文件。wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件并从开头开始编辑即原有内容会被删除。如果该文件不存在创建新文件。一般用于非文本文件如图片等。w打开一个文件用于读写。如果该文件已存在则打开文件并从开头开始编辑即原有内容会被删除。如果该文件不存在创建新文件。wb以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件并从开头开始编辑即原有内容会被删除。如果该文件不存在创建新文件。一般用于非文本文件如图片等。a打开一个文件用于追加。如果该文件已存在文件指针将会放在文件的结尾。也就是说新的内容将会被写入到已有内容之后。如果该文件不存在创建新文件进行写入。ab以二进制格式打开一个文件用于追加。如果该文件已存在文件指针将会放在文件的结尾。也就是说新的内容将会被写入到已有内容之后。如果该文件不存在创建新文件进行写入。a打开一个文件用于读写。如果该文件已存在文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在创建新文件用于读写。ab以二进制格式打开一个文件用于追加。如果该文件已存在文件指针将会放在文件的结尾。如果该文件不存在创建新文件用于读写。 默认为文本模式如果要以二进制模式打开加上 b 。 open() 函数生成的是 file 对象下边是 file 对象的常用方法 方法描述file.read([size])从文件读取指定的字节数如果未给定或为负则读取所有。file.readline([size])读取整行包括 “\n” 字符。file.readlines([sizeint])读取所有行并返回列表若给定sizeint0则是设置一次读多少字节这是为了减轻读取压力。file.write(str)将字符串写入文件返回的是写入的字符长度。file.writelines(sequence)向文件写入一个序列字符串列表如果需要换行则要自己加入每行的换行符。file.close()关闭文件。关闭后文件不能再进行读写操作。file.flush()刷新文件内部缓冲直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。file.fileno()返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。file.isatty()如果文件连接到一个终端设备返回 True否则返回 False。file.next()返回文件下一行。file.seek(offset[, whence])设置文件当前位置file.tell()返回文件当前位置。file.truncate([size])截取文件截取的字节通过size指定默认为当前文件位置。file.readable()判断文件是否可读 一、文件的打开 在python中通过 open() 函数打开一个文件函数内主要参数是文件的位置字节编码和打开模式 f open(file rD:\python\PycharmProjects\pythonProject001\aa.text,moder,encodingutf-8)如果想要创建一个文件则mode设置成 w f open(file rD:\python\PycharmProjects\pythonProject001\bb.text,modew,encodingutf-8) 二、文件的读取 通过open() 创建一个可读取的文件之后通过 read() 读取文件的内容 需要注意的是在文件的读取是有连续性和不可重复性的也就是读取过的内容不会被再次读取 aa.text文件内容为第一行aa 第二行bb 第三行ccf open(file rD:\python\PycharmProjects\pythonProject001\aa.text,moder,encodingutf-8) print(f.read())# 执行结果为第一行aa 第二行bb 第三行cc 读取指定字节数 print(f.read(9))# 打印结果 第一行aa 第二行可以通过 readline() 一次读取一行数据 aa.text文件内容为第一行aa 第二行bb 第三行ccf open(file rD:\python\PycharmProjects\pythonProject001\aa.text,moder,encodingutf-8)print(f.read(4)) # 第一行aprint(f.readline()) # aprint(f.readline()) # 第二行bb如果想读取的数据返回一个集合可以使用 readlines() aa.text文件内容为第一行aa 第二行bb 第三行ccf open(file rD:\python\PycharmProjects\pythonProject001\aa.text,moder,encodingutf-8)print(f.readlines()) # [第一行aa\n, 第二行bb\n, 第三行cc]三、文件的写入 打开一个可写的 file 后 通过 write() 写入内容 # 创建文件 fw open(file rD:\python\PycharmProjects\pythonProject001\bb.text,modew,encodingutf-8)fw.write(这是我手动输入的内容) # 写入内容# 读取文件内容 fr open(file rD:\python\PycharmProjects\pythonProject001\bb.text,moder,encodingutf-8)print(fr.read()) # 这是我手动输入的内容四、文件的关闭 文件打开之后我们操作完数据之后一定要关闭的因此我们可以通过 try - finally 来保证文件一定执行关闭操作 try:fr open(file rD:\python\PycharmProjects\pythonProject001\aa.text,moder,encodingutf-8)print(fr.read()) finally:fr.close()一般我们可以通过使用 with 关键字来保证文件的关闭它内部自动帮我们做了 try - finally 操作具体用法如下 with open(file rD:\python\PycharmProjects\pythonProject001\aa.text,moder,encodingutf-8) as f :print(f.read())五、打开一个可读可写的文件 在 Python 中你可以使用内置的 open 函数来打开一个文件并指定文件的打开模式为可读可写。你可以使用模式 ① ‘r’读取和写入文件必须存在 ② ‘w’写入和读取会覆盖文件内容 ③ ‘a’追加和读取在文件末尾添加内容。 下面是一些示例代码展示了如何使用这些模式 使用 ‘r’ 模式文件必须存在 try:with open(example.txt, r) as file:content file.read()print(文件内容, content)file.seek(0) # 移动到文件开头file.write(新的内容\n) except FileNotFoundError:print(文件不存在无法使用 r 模式打开)使用 ‘w’ 模式会覆盖文件内容 with open(example.txt, w) as file:file.write(这是一些新的内容\n)file.seek(0) # 移动到文件开头content file.read()print(文件内容, content)使用 ‘a’ 模式在文件末尾添加内容 with open(example.txt, a) as file:file.write(追加的内容\n)file.seek(0) # 移动到文件开头content file.read()print(文件内容, content)六、文件目录的简单操作-os模块 python中对文件目录的操作主要是通过os模块的功能来实现的下面来演示os模块对目录的相关操作 1、文件重命名操作-os.rename(“原名”,“新名”) import os# 我对文件重命名 os.rename(abc.text,abc2.text)2、创建文件夹-os.mkdir(“目录地址”) 注意一次只能创建一级目录下边的例子中abc是新需要创建的目录 # 创建文件夹 , 一次只能创建一级目录 # os.mkdir(/Users/wangtianyu/PycharmProjects/pythonProject/abc)3、删除文件夹-os.rmdir(“目录地址”) # 删除文件夹 os.rmdir(/Users/wangtianyu/PycharmProjects/pythonProject/abc)4、删除文件-os.remove(“目录地址”)或os.unlink(“目录地址”) # 删除文件 os.remove(abc2.text) os.unlink(abc.text)5、获取当前路径-os.getcwd() # 获取当前路径 print(os.getcwd()) # /Users/wangtianyu/PycharmProjects/pythonProject6、切换当前路径-os.chdir(“切换命令”) # 获取当前路径 print(os.getcwd()) # /Users/wangtianyu/PycharmProjects/pythonProjectos.chdir(../) # 同liunx命令切换到上级目录print(os.getcwd()) # /Users/wangtianyu/PycharmProjects7、获取路径下的目录列表-os.listdir(“指定路径”) 函数如参不写默认取当前路径 print(os.listdir()) # 获取当前路径下的目录列表# [第二章.py, 第一章.py, os_test.py, .venv, .idea]print(os.listdir(/Users/wangtianyu/PycharmProjects)) # 获取指定路径下的目录列表# [pythonProject]8、更多用法 方法解释os.access(path, mode)检验权限模式os.chdir(path)改变当前工作目录os.chflags(path, flags)设置路径的标记为数字标记。os.chmod(path, mode)更改权限os.chown(path, uid, gid)更改文件所有者os.chroot(path)改变当前进程的根目录os.close(fd)关闭文件描述符 fdos.closerange(fd_low, fd_high)关闭所有文件描述符从 fd_low (包含) 到 fd_high (不包含), 错误会忽略os.dup(fd)复制文件描述符 fdos.dup2(fd, fd2)将一个文件描述符 fd 复制到另一个 fd2os.fchdir(fd)通过文件描述符改变当前工作目录os.fchmod(fd, mode)改变一个文件的访问权限该文件由参数fd指定参数mode是Unix下的文件访问权限。os.fchown(fd, uid, gid)修改一个文件的所有权这个函数修改一个文件的用户ID和用户组ID该文件由文件描述符fd指定。os.fdatasync(fd)强制将文件写入磁盘该文件由文件描述符fd指定但是不强制更新文件的状态信息。os.fdopen(fd[, mode[, bufsize]])通过文件描述符 fd 创建一个文件对象并返回这个文件对象os.fpathconf(fd, name)返回一个打开的文件的系统配置信息。name为检索的系统配置的值它也许是一个定义系统值的字符串这些名字在很多标准中指定POSIX.1, Unix 95, Unix 98, 和其它。os.fstat(fd)返回文件描述符fd的状态像stat()。os.fstatvfs(fd)返回包含文件描述符fd的文件的文件系统的信息像 statvfs()os.fsync(fd)强制将文件描述符为fd的文件写入硬盘。os.ftruncate(fd, length)裁剪文件描述符fd对应的文件, 所以它最大不能超过文件大小。os.getcwd()返回当前工作目录os.getcwdu()返回一个当前工作目录的Unicode对象os.isatty(fd)如果文件描述符fd是打开的同时与tty(-like)设备相连则返回true, 否则False。os.lchflags(path, flags)设置路径的标记为数字标记类似 chflags()但是没有软链接os.lchmod(path, mode)修改连接文件权限os.lchown(path, uid, gid)更改文件所有者类似 chown但是不追踪链接。os.link(src, dst)创建硬链接名为参数 dst指向参数 srcos.listdir(path)返回path指定的文件夹包含的文件或文件夹的名字的列表。os.lseek(fd, pos, how)设置文件描述符 fd当前位置为pos, how方式修改: SEEK_SET 或者 0 设置从文件开始的计算的pos; SEEK_CUR或者 1 则从当前位置计算; os.SEEK_END或者2则从文件尾部开始. 在unixWindows中有效os.lstat(path)像stat(),但是没有软链接os.major(device)从原始的设备号中提取设备major号码 (使用stat中的st_dev或者st_rdev field)。os.makedev(major, minor)以major和minor设备号组成一个原始设备号os.makedirs(path[, mode])递归文件夹创建函数。像mkdir(), 但创建的所有intermediate-level文件夹需要包含子文件夹。os.minor(device)从原始的设备号中提取设备minor号码 (使用stat中的st_dev或者st_rdev field )。os.mkdir(path[, mode])以数字mode的mode创建一个名为path的文件夹.默认的 mode 是 0777 (八进制)。os.mkfifo(path[, mode])创建命名管道mode 为数字默认为 0666 (八进制)os.mknod(filename[, mode0600, device])创建一个名为filename文件系统节点文件设备特别文件或者命名pipe。os.open(file, flags[, mode])打开一个文件并且设置需要的打开选项mode参数是可选的os.openpty()打开一个新的伪终端对。返回 pty 和 tty的文件描述符。os.pathconf(path, name)返回相关文件的系统配置信息。os.pipe()创建一个管道. 返回一对文件描述符(r, w) 分别为读和写os.popen(command[, mode[, bufsize]])从一个 command 打开一个管道os.read(fd, n)从文件描述符 fd 中读取最多 n 个字节返回包含读取字节的字符串文件描述符 fd对应文件已达到结尾, 返回一个空字符串。os.readlink(path)返回软链接所指向的文件os.remove(path)删除路径为path的文件。如果path 是一个文件夹将抛出OSError; 查看下面的rmdir()删除一个 directory。os.removedirs(path)递归删除目录。os.rename(src, dst)重命名文件或目录从 src 到 dstos.renames(old, new)递归地对目录进行更名也可以对文件进行更名。os.rmdir(path)删除path指定的空目录如果目录非空则抛出一个OSError异常。os.stat(path)获取path指定的路径的信息功能等同于C API中的stat()系统调用。os.stat_float_times([newvalue])决定stat_result是否以float对象显示时间戳os.statvfs(path)获取指定路径的文件系统统计信息os.symlink(src, dst)创建一个软链接os.tcgetpgrp(fd)返回与终端fd一个由os.open()返回的打开的文件描述符关联的进程组os.tcsetpgrp(fd, pg)设置与终端fd一个由os.open()返回的打开的文件描述符关联的进程组为pg。os.ttyname(fd)返回一个字符串它表示与文件描述符fd 关联的终端设备。如果fd 没有与终端设备关联则引发一个异常。os.unlink(path)删除文件os.utime(path, times)返回指定的path文件的访问和修改的时间。os.walk(top[, topdownTrue[, οnerrοrNone[, followlinksFalse]]])输出在文件夹中的文件名通过在树中游走向上或者向下。os.write(fd, str)写入字符串到文件描述符 fd中. 返回实际写入的字符串长度os.path 模块获取文件的属性信息。 第六章、多线程 一、线程 Python提供了threading模块来支持线程的创建、同步和通信。threading模块的设计基于Java的线程模型但也有一些不同之处。例如在Java中锁和条件变量是每个对象的基础特性而在Python中这些被独立成了单独的对象如threading.Lock()锁threading.Condition()条件变量。 1、线程的创建与启动 Python3中使用的是 threading.Thread来创建线程通过调用线程的start()启动线程 其中Thread函数的入参包括以下 group线程组暂时未使用保留为将来的扩展。target线程将要执行的目标函数。name线程的名称。args目标函数的参数以元组形式传递。kwargs目标函数的关键字参数以字典形式传递。daemon指定线程是否为守护线程。 import threading import timedef threa_test():time.sleep(1) # 新线程阻塞两秒为了让主线程先执行完方便看效果print(新线程执行)td threading.Thread(targetthrea_test) # 创建一个线程 注意传入的函数名不带括号td.start() # 启动新线程print(主线程执行) # 执行结果 主线程执行 新线程执行join(): a.join() a为子线主线程进行阻塞等待a执行完毕之后再执行主线程 import threading import timedef threa_test():time.sleep(1) # 新线程阻塞两秒为了让主线程先执行完方便看效果print(新线程执行)td threading.Thread(targetthrea_test) # 创建一个线程 注意传入的函数名不带括号td.start() print(主线程join前执行) td.join() print(主线程join后执行) # 输出 主线程join前执行 新线程执行 主线程join后执行 daemon:守护线程 默认为False设置为True表示主线程如果结束则子线程也结束不管子线程是否执行完 import threading import timedef threa_test():for i in range(5):time.sleep(1) # 阻塞1秒print(新线程执行,1)td threading.Thread(targetthrea_test,daemonTrue) # 设置为守护线程 # td.daemon True 也可以通过设置属性的方式设置为守护线程td.start() time.sleep(3) # 主线程阻塞3秒 print(主线程执行完毕) # 输出 新线程执行 1 新线程执行 1 主线程执行完毕从上边的例子可以看出主线程执行完毕的时候子线程还没有输出完只输出到了2但使因为设置了守护线程也跟着主线程的结束而结束 2、线程的同步与锁 在多线程编程中为了避免多个线程同时访问共享资源导致数据混乱或不一致的问题可以使用互斥锁Lock来实现线程同步。 在没有锁的情况下多线程会出现共享资源混乱的情况 import threading import timecon 10 # 假设库存为10 lock Lock() def pop_con():global conif con 0: # 如果有库存time.sleep(0.0001) # 阻塞 0.0001秒模拟减库存操作con - 1 # 库存减一print(卖出一个)else:print(卖完了)if __name__ __main__:for i in range(15): # 假设有15个用户同时下单threading.Thread(targetpop_con,daemonFalse).start() # 输出 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖完了 卖完了 卖出一个从上边的例子可以看出库存只有 10 但是却卖出了13个物品这样就出现了超卖问题所以在出现多个线程争抢同一个资源的时候就需要加上互斥锁来保证不会出现超卖的现象 python中 threading 的 Lock 就是用来解决都线程共享资源的问题的 import threading from threading import Lock import timecon 10 # 假设库存为10 lock Lock() def pop_con():lock.acquire() # 加锁global conif con 0:time.sleep(0.0001) # 阻塞 0.001秒模拟减库存操作con - 1print(卖出一个)else:print(卖完了)lock.release() # 释放锁if __name__ __main__:for i in range(15):threading.Thread(targetpop_con,daemonFalse).start() # 输出 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖出一个 卖完了 卖完了 卖完了 卖完了 卖完了加锁的时候必须要释放锁所以我们释放锁的代码需要通过写到 try finally中 def pop_con():try:lock.acquire()passfinally:lock.release()因此我们使可以使用 whit 关键字来简化写法的它自动帮我们做了加锁和解锁的操作 最终写法为 import threading from operator import ifloordiv from threading import Lock import timecon 10 # 假设库存为10 lock Lock() def pop_con():with lock:global conif con 0:time.sleep(0.0001) # 阻塞 0.001秒模拟减库存操作con - 1print(卖出一个)else:print(卖完了)if __name__ __main__:for i in range(15):threading.Thread(targetpop_con,daemonFalse).start()3、线程间通信 线程间通信可以使用队列Queue来实现。Queue是线程安全的数据结构可以在多个线程之间安全地传递数据。 示例代码 import threading import queuedef producer(q):for i in range(5):q.put(i)def consumer(q):while not q.empty():item q.get()print(Consumed:, item)# 创建队列 q queue.Queue() # 创建生产者和消费者线程 producer_thread threading.Thread(targetproducer, args(q,)) consumer_thread threading.Thread(targetconsumer, args(q,)) # 启动线程 producer_thread.start() consumer_thread.start() # 等待线程执行完成 producer_thread.join() consumer_thread.join()在这个示例中生产者线程向队列中放入数据消费者线程从队列中取出数据进行消费实现了线程间的通信。 Python 的 Queue 模块中提供了同步的、线程安全的队列类包括FIFO先入先出)队列QueueLIFO后入先出队列LifoQueue和优先级队列 PriorityQueue。 这些队列都实现了锁原语能够在多线程中直接使用可以使用队列来实现线程间的同步。 Queue 模块中的常用方法: Queue.qsize() 返回队列的大小Queue.empty() 如果队列为空返回True,反之FalseQueue.full() 如果队列满了返回True,反之FalseQueue.full 与 maxsize 大小对应Queue.get([block[, timeout]])获取队列timeout等待时间Queue.get_nowait() 相当Queue.get(False)Queue.put(item) 写入队列timeout等待时间Queue.put_nowait(item) 相当Queue.put(item, False)Queue.task_done() 在完成一项工作之后Queue.task_done()函数向任务已经完成的队列发送一个信号Queue.join() 实际上意味着等到队列为空再执行别的操作 4、线程池 使用线程池可以方便地管理线程控制线程数量并提交任务。Python的concurrent.futures模块提供了ThreadPoolExecutor类来创建线程池。 示例代码 from concurrent.futures import ThreadPoolExecutor import timedef task(n):print(fTask {n} started)time.sleep(2)return fTask {n} completed# 创建ThreadPoolExecutor with ThreadPoolExecutor(max_workers3) as executor:# 提交任务给线程池future1 executor.submit(task, 1)future2 executor.submit(task, 2)future3 executor.submit(task, 3)# 获取任务执行结果print(future1.result())print(future2.result())print(future3.result())在这个示例中创建了一个最大容纳3个线程的线程池并提交了三个任务给线程池执行。 5、自定义创建新线程 我们可以通过直接从 threading.Thread 继承创建一个新的子类并实例化后调用 start() 方法启动新线程即它调用了线程的 run() 方法 #!/usr/bin/python3import threading import timeexitFlag 0class myThread (threading.Thread):def __init__(self, threadID, name, delay):threading.Thread.__init__(self)self.threadID threadIDself.name nameself.delay delaydef run(self):print (开始线程 self.name)print_time(self.name, self.delay, 2)print (退出线程 self.name)def print_time(threadName, delay, counter):while counter:if exitFlag:threadName.exit()time.sleep(delay)print (%s: %s % (threadName, time.ctime(time.time())))counter - 1# 创建新线程 thread1 myThread(1, Thread-1, 1) thread2 myThread(2, Thread-2, 2)# 开启新线程 thread1.start() thread2.start() thread1.join() thread2.join() print (退出主线程) # 输出 开始线程Thread-1 开始线程Thread-2 Thread-1: Sun Feb 16 12:56:21 2025 Thread-2: Sun Feb 16 12:56:22 2025 Thread-1: Sun Feb 16 12:56:22 2025 退出线程Thread-1 Thread-2: Sun Feb 16 12:56:24 2025 退出线程Thread-2 退出主线程 二、进程 进程是资源分配的最小单元线程是cpu调度的最小单元一个进程包含多个线程。 ps进程之间不共享全局变量 在Python中进程可以通过multiprocessing模块来实现。multiprocessing模块支持子进程的创建、通信和同步等功能提供了Process、Queue、Pipe、Lock等组件。 1、进程的创建与启动 创建进程是通过multiprocessing模块的Process类来创建的主要包括以下参数 group指定进程组name进程名字target子进程要执行任务的函数名args子进程函数的入参以元组的形式传递kwargs目标函数的关键字参数以字典形式传递。 下边就是创建进程的例子 import multiprocessing import os import timedef test():for _ in range(5):time.sleep(1)print(子进程执行,os.getpid(),父进程为:,os.getppid())if __name__ __main__:up multiprocessing.Process(targettest)up.start()time.sleep(3)print(主进程执行,os.getpid(),父进程为:,os.getppid()) # 执行结果 子进程执行 22604 父进程为: 22400 子进程执行 22604 父进程为: 22400 主进程执行 22400 父进程为: 14620 子进程执行 22604 父进程为: 22400 子进程执行 22604 父进程为: 22400 子进程执行 22604 父进程为: 22400os.getpid()获取进程号os.getppid()获取父进程号 2、进程属性与方法 属性 name当前进程的别名。pid当前进程的进程号PID。daemon如果设置为True则主进程结束时子进程会随之结束。 方法 start()启动子进程实例。join(timeout)等待子进程执行结束。timeout是可选的超时时间。is_alive()判断进程是否还存活。terminate()立即终止子进程。run()进程启动时运行的方法正是它调用的target指定的函数。 3、进程间的通信与同步 通信 队列Queuemultiprocessing.Queue提供了一个线程和进程安全的队列用于在多个进程之间传递数据。管道Pipemultiprocessing.Pipe返回一个连接的对象Connection用于两个进程之间的双向通信。 同步 锁Lockmultiprocessing.Lock提供了进程间的同步机制防止多个进程同时访问共享资源。事件Eventmultiprocessing.Event用于允许多个进程或线程等待某个事件的发生。条件变量Conditionmultiprocessing.Condition允许一个或多个进程等待某个条件为真再继续执行。信号量Semaphoremultiprocessing.Semaphore是计数信号量用于控制对共享资源的访问数量。 4、进程池Pool 当需要创建大量的子进程时可以使用Pool来管理这些进程。Pool允许用户指定一个最大进程数并自动管理这些进程的创建和销毁。 创建Pool 使用Pool(processesNone, initializerNone, initargs(), maxtasksperchildNone)来创建一个进程池。processes参数指定池中进程的最大数量。initializer和initargs参数用于在每个工作进程启动时执行一个指定的函数及其参数。maxtasksperchild参数指定每个工作进程可以执行的任务数之后工作进程会被替换。 使用Pool apply(func[, args[, kwds]])同步执行一个函数并等待结果。map(func, iterable[, chunksize])将一个可迭代对象中的每个元素传递给一个函数并收集结果。async_apply(func[, args[, kwds]])异步执行一个函数。async_map(func, iterable[, chunksize])异步地将一个可迭代对象中的每个元素传递给一个函数。 5、示例代码 以下是一个简单的示例代码展示了如何使用multiprocessing模块创建进程、传递参数、等待进程结束以及使用队列进行进程间通信 import multiprocessing import timedef worker(interval, name, queue):print(f{name}【start】)time.sleep(interval)print(f{name}【end】)queue.put(f{name}的结果)if __name__ __main__:queue multiprocessing.Queue()p1 multiprocessing.Process(targetworker, args(2, 两点水1, queue))p2 multiprocessing.Process(targetworker, args(3, 两点水2, queue))p3 multiprocessing.Process(targetworker, args(4, 两点水3, queue))p1.start()p2.start()p3.start()for p in [p1, p2, p3]:p.join()while not queue.empty():print(queue.get())以上代码创建了三个子进程每个子进程执行worker函数并将结果放入队列中。主进程等待所有子进程执行完毕后从队列中读取并打印结果。 三、协程 协程又称微线程是一种用户态的轻量级线程其切换是由用户程序自己控制调度的而非操作系统。因此协程的切换开销非常小适用于I/O密集型任务。协程能够保留上次调用时的状态对协程来说程序员就是他的上帝想让他执行到哪里就执行到哪里。 协程存在的意义 开销协程的切换开销非常小因为它们是由程序员显式控制的并且不涉及内核态与用户态之间的切换。相比之下线程和进程的切换开销较大。并发性协程可以实现高效的并发执行因为它们可以在单个线程内交替运行。然而由于GIL全局解释器锁的存在Python中的多线程在CPU密集型任务上可能并不会带来性能上的提升。对于I/O密集型任务多线程和协程都可以显著提高程序的并发性和性能。多进程则不受GIL的限制但进程间通信和同步的开销较大。编程模型协程的编程模型更加直观和易于理解因为它们遵循了同步编程的直观逻辑但具有异步执行的能力。多线程和多进程的编程模型则更加复杂因为它们需要处理线程或进程间的同步和通信问题。 1、greenlet创建协程 greenlet 是 Python 中一个轻量级的协程库它提供了一种在单个线程中协作执行多个函数的方式。与更高级的协程库如 asyncio相比greenlet 更加底层和简单主要提供了基本的上下文切换功能。手动切换协程 greenlet 的核心是两个类greenlet.greenlet 和 greenlet.getcurrent()。greenlet.greenlet 用于创建新的 greenlet而 greenlet.getcurrent() 用于获取当前正在执行的 greenlet。 from greenlet import greenletdef a():print(男孩我想对你说三个字)g2.switch() # 手动切换协程print(男孩我饿了)g2.switch() # 手动切换协程def b():print(女孩我也爱你)g1.switch() # 手动切换协程print(女孩好吧。。。)g1 greenlet(a) # 创建协程g1 g2 greenlet(b) # 创建协程g2g1.switch() # 启动协程 # 输出 男孩我想对你说三个字 女孩我也爱你 男孩我饿了 女孩好吧。。。 注意事项 手动切换与 asyncio 不同greenlet 不会自动管理协程的调度。你需要显式地调用 switch() 方法来切换 greenlet。异常处理在一个 greenlet 中抛出的异常不会自动传播到另一个 greenlet。你需要在每个 greenlet 内部处理异常。死锁如果两个 greenlet 互相等待对方切换会导致死锁。因此设计 greenlet 时需要小心避免这种情况。虽然 greenlet 提供了基本的协程功能但它通常不直接用于生产环境。更高级的库如 gevent 和 asyncio提供了更丰富的功能和更好的错误处理机制。gevent 是基于 greenlet 的一个高级库它添加了事件循环和网络 I/O 的支持。 2、gevent创建协程 Gevent是基于libev与greenlet实现的。其中libev是一个高性能的事件循环库greenlet是Python中的一个轻量级协程库。Gevent通过结合这两个库实现了自动切换协程的功能从而提高了程序的执行效率。 1、基本使用 创建协程 使用gevent.spawn()函数可以创建一个协程。该函数接受一个函数作为参数并可以传递任意数量的位置参数和关键字参数。创建协程后它将立即开始执行。启动 使用join()方法可以等待协程完成。调用join()方法将阻塞当前线程直到协程执行完毕。获取返回值 协程执行完毕后可以使用value属性来获取其返回值如果有的话。优势 Gevent的主要优势在于它能够自动切换协程特别是在遇到IO操作时。当协程遇到IO阻塞时如网络请求、文件读写等Gevent会自动切换到其他协程从而充分利用CPU资源。 import geventdef func(name):for i in range(3):print(name,打印,i)gevent.sleep(1) # 模拟io耗时操作return 返参nameg1 gevent.spawn(func,协程g1) # 创建协程g1 g2 gevent.spawn(func,协程g2) # 创建协程g2g1.join() # 启动协程g1 g1.join() # 启动协程g2print(g1执行最终返回结果:,g1.value) # 打印协程 g1 的返参 print(g2执行最终返回结果:,g2.value) # 打印协程 g1 的返参# 执行结果 协程g1 打印 0 协程g2 打印 0 协程g1 打印 1 协程g2 打印 1 协程g1 打印 2 协程g2 打印 2 g1执行最终返回结果: 返参协程g1 g2执行最终返回结果: 返参协程g22、joinall 的使用 如果一次启动多个协程可以使用gevent.greenlet.joinall函数入参为列表元素为创建的协程用法如下 import gevent from gevent.greenlet import joinalldef func(name):for i in range(3):print(name,打印,i)gevent.sleep(1) # 模拟io耗时操作return 返参nameg1 gevent.spawn(func,协程g1) # 创建协程g1 g2 gevent.spawn(func,协程g2) # 创建协程g2joinall([g1,g2]) # 一次启动多个协程3、 给程序打补丁 monkey 为了使Gevent能够识别并处理程序中的IO阻塞操作你需要使用monkey.patch_all()函数来给程序打补丁。这个函数会替换Python标准库中的一些模块如socket、ssl、time等使它们支持Gevent的协程切换。 import gevent from gevent import monkey import timemonkey.patch_all() # 打补丁def func(name):for i in range(3):print(name,打印,i)time.sleep(1) # 这里使用的是time.sleep()但由于打了补丁所以也会被Gevent识别并处理return 返参nameg1 gevent.spawn(func,协程g1) # 创建协程g1 g2 gevent.spawn(func,协程g2) # 创建协程g2g1.join() # 启动协程g1 g1.join() # 启动协程g2print(g1执行最终返回结果:,g1.value) # 打印协程 g1 的返参 print(g2执行最终返回结果:,g2.value) # 打印协程 g1 的返参 # 输出 协程g1 打印 0 协程g2 打印 0 协程g1 打印 1 协程g2 打印 1 协程g1 打印 2 协程g2 打印 2 g1执行最终返回结果: 返参协程g1 g2执行最终返回结果: 返参协程g2# 如果没有 monkey.patch_all() # 打补丁 输出的结果为 协程g1 打印 0 协程g1 打印 1 协程g1 打印 2 协程g2 打印 0 协程g2 打印 1 协程g2 打印 2 g1执行最终返回结果: 返参协程g1 g2执行最终返回结果: 返参协程g23、 高级用法 使用锁 在协程中你可能需要使用锁来保护共享资源。Gevent提供了多种锁机制如Semaphore信号量、Lock互斥锁等。 import gevent from gevent import lock from gevent.greenlet import joinallrlock lock.RLock()def func(name):with rlock:for i in range(3):print(name,打印,i)gevent.sleep(1) # 这里使用的是time.sleep()但由于打了补丁所以也会被Gevent识别并处理return 返参nameg1 gevent.spawn(func,协程g1) # 创建协程g1 g2 gevent.spawn(func,协程g2) # 创建协程g2joinall([g1,g2])print(g1执行最终返回结果:,g1.value) # 打印协程 g1 的返参 print(g2执行最终返回结果:,g2.value) # 打印协程 g1 的返参# 输出 协程g1 打印 0 协程g1 打印 1 协程g1 打印 2 协程g2 打印 0 协程g2 打印 1 协程g2 打印 2 g1执行最终返回结果: 返参协程g1 g2执行最终返回结果: 返参协程g2使用队列 Gevent还提供了队列机制用于在协程之间传递数据。你可以使用gevent.queue.Queue来创建一个线程安全的队列。 from gevent.queue import Queue import geventq Queue() # 创建一个队列def producer():for i in range(5):q.put(i)print(f生产一个 {i})gevent.sleep(1)def consumer():for i in range(5):item q.get()print(f消费一个 {item})gevent.joinall([gevent.spawn(producer), gevent.spawn(consumer)]) # 输出 生产一个 0 消费一个 0 生产一个 1 消费一个 1 生产一个 2 消费一个 2 生产一个 3 消费一个 3 生产一个 4 消费一个 4网络编程 Gevent非常适合用于网络编程。你可以使用它来创建高性能的TCP/UDP服务器和客户端。 from gevent import monkey; monkey.patch_all() from socket import * import geventdef server(server_ip, port):s socket(AF_INET, SOCK_STREAM)s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)s.bind((server_ip, port))s.listen(5)while True:conn, addr s.accept()gevent.spawn(talk, conn, addr)def talk(conn, addr):try:while True:res conn.recv(1024)print(fClient {addr[0]}:{addr[1]} msg: {res})conn.send(res.upper())except Exception as e:print(e)finally:conn.close()if __name__ __main__:server(127.0.0.1, 8080)上面的例子展示了如何使用Gevent创建一个简单的TCP服务器。当服务器接受到客户端的连接时它会创建一个新的协程来处理该连接。 3、asyncio创建协程 asyncio是Python标准库中的一个模块提供了使用协程构建并发应用的工具。它使用一种单线程单进程的方式实现并发应用的各个部分彼此合作可以显式地切换任务。这一般会在程序阻塞I/O操作的时候发生上下文切换如等待读写文件或请求网络。同时asyncio也支持调度代码在将来的某个特定事件运行从而支持一个协程等待另一个协程完成以处理系统信号和识别其他一些事件。 3.1、协程的定义与运行 定义协程协程的定义需要使用async def语句。例如async def do_some_work(x): pass这里的do_some_work便是一个协程函数。准确来说do_some_work是一个协程函数可以通过asyncio.iscoroutinefunction来验证。运行协程调用协程函数协程并不会开始运行只是返回一个协程对象。要让这个协程对象运行有两种方式 在另一个已经运行的协程中用await等待它。通过ensure_future函数计划它的执行。 可以通过asyncio.get_event_loop()获取当前线程的事件循环然后将协程对象交给loop.run_until_complete()协程对象随后会在loop里得到运行。例如 import asyncioasync def do_some_work(x):print(Waiting str(x))await asyncio.sleep(x)loop asyncio.get_event_loop() loop.run_until_complete(do_some_work(3))这段代码会输出“Waiting 3”然后等待3秒钟后程序结束。 3.2、 await关键字与异步操作 在协程中可以使用await关键字来等待其他异步函数或某个任务完成。await只能用在async定义的函数内部。例如await asyncio.sleep(x)就是等待x秒。 3.3、 事件循环 事件循环是asyncio提供的“中央处理设备”它负责注册、执行和取消延迟调用超时创建可用于多种类型的通信的服务端和客户端的Transports启动进程以及相关的和外部通信程序的Transports将耗时函数调用委托给一个线程池等。 事件循环是一种处理多并发量的有效方式。在asyncio中程序会开启一个无限的循环把一些函数注册到事件循环上。当满足事件发生的时候调用相应的协程函数。 3.4、 Future与Task Future是一个数据结构表示还未完成的工作结果。事件循环可以监视Future对象是否完成从而允许应用的一部分等待另一部分完成一些工作。Task是Future的一个子类它知道如何包装和管理一个协程的执行。任务所需的资源可用时事件循环会调度任务运行并生成一个结果从而可以由其他协程消费。 3.5、多个协程的并发执行 实际项目中往往有多个协程需要同时在一个loop里运行。为了把多个协程交给loop需要借助asyncio.gather函数。例如 import asyncio import randomasync def async_task(name):print(f{name}开始工作...)await asyncio.sleep(random.uniform(1, 3))print(f{name}工作完成!)async def main():tasks []for i in range(5):task_name f任务{i 1}tasks.append(async_task(task_name))await asyncio.gather(*tasks)asyncio.run(main())这段代码创建了5个异步任务并使用asyncio.gather来并发执行所有任务直到所有任务完成。 3.6、 回调函数 假如协程是一个IO的读操作等它读完数据后希望得到通知以便下一步数据的处理。这一需求可以通过往Future添加回调来实现。例如 def done_callback(futu):print(Done)futu asyncio.ensure_future(do_some_work(3)) futu.add_done_callback(done_callback) loop.run_until_complete(futu)3.7、 asyncio的其他功能 除了上述基本功能外asyncio还提供了许多其他功能如 异步上下文管理器可以使用async with语句来管理异步资源的上下文。信号处理可以使用asyncio.signal_handler来添加信号处理函数。子进程可以使用asyncio.create_subprocess_exec等函数来创建和管理子进程。 3.8、 注意事项 确保在使用await时调用的是异步函数。使用asyncio.sleep()来模拟等待时间而不是使用普通的time.sleep()因为后者会阻塞事件循环。根据实际需要调整并发任务的数量和类型以优化性能。 四、进程-线程-协程之前的异同 1、相同点 都是并发编程的实现方式线程、进程和协程都可以用来实现程序的并发执行提高程序的运行效率。都可以共享资源在一定程度上进程间可以通过特定的通信方式进行数据交换线程共享同一进程的内存空间而协程则共享同一线程的资源。 2、不同点 定义与资源拥有 进程 定义进程是操作系统资源分配的基本单位每个进程都拥有独立的内存空间和系统资源。资源拥有进程拥有独立的内存空间、数据栈等系统资源。 线程 定义线程是操作系统中调度和执行的基本单位被包含在进程之中。一个进程中可以包含一个或多个线程所有线程共享该进程的资源。资源拥有线程不拥有独立的系统资源但拥有自己的执行堆栈和程序计数器。线程共享进程的内存空间和系统资源。 协程 定义协程是一种用户态的轻量级线程其调度完全由用户控制。协程拥有自己的寄存器上下文和栈。资源拥有协程不拥有独立的系统资源只拥有在运行中必不可少的资源如寄存器上下文和栈并共享所属线程的资源。 执行与调度 进程 执行进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动。调度进程的创建、销毁和调度开销相对较大因为它们拥有独立的内存空间和系统资源。 线程 执行线程是进程内的一个执行单元可以并发执行。调度线程的调度开销较小因为线程共享同一进程的内存空间和系统资源。线程是处理器调度的基本单位。 协程 执行协程在单一线程内部实现并发不存在多线程中常见的数据竞争等线程同步问题。当协程遇到I/O操作时它会将控制权让给其他协程直到I/O操作完成。调度协程的调度完全由用户控制没有线程切换的开销。协程的切换由程序自身控制因此切换速度非常快。 适用场景与性能 进程 适用场景多进程一般使用在CPU密集型的程序上因为进程拥有独立的内存空间和系统资源可以充分利用多核CPU的并行计算能力。性能进程间的通信和同步相对复杂开销较大。 线程 适用场景多线程适合用于既有计算密集型任务又有I/O密集型任务的场景。多线程可以完成一些I/O密集型并发操作如文件读写、网络请求等。性能多线程的优势是切换快、资源消耗低。但由于存在数据共享数据同步如通过锁会是多线程编程的一个常见问题。此外由于Python全局解释器锁GIL的存在多线程在执行CPU密集型任务时性能得不到明显提升。 协程 适用场景协程适合用于I/O密集型任务。在遇到I/O操作时协程会将控制权让给其他协程从而充分利用等待时间。性能协程的执行效率极高因为子程序切换不是线程切换而是由程序自身控制。此外协程不需要多线程的锁机制因为只有一个线程在执行。这使得协程在I/O密集型任务上具有显著的性能优势。
http://www.dnsts.com.cn/news/251401.html

相关文章:

  • 做课件用这15大网站济宁网站建设优化
  • 宁波建网站找哪家有比wordpress更好的吗
  • 泉州网站建设有哪些网站搭建一般要多少钱
  • 关于加强网站建设的意见全球首个完全响应式网站自助建设平台在中国诞生
  • 网站流量分析系统品牌设计公司名称
  • 南宁百度做网站多少钱wordpress批量编辑
  • 网站注册和进入asp建网站麻烦拍照备案审核多久
  • 建设网站设计公司兰州网站优化服务
  • 安徽手机版建站系统网站建设、百度推广
  • 上海网站设计公司wordpress 修改主题名
  • 网站制作多少钱王烨娟
  • 电商建网站运营网站建设在哪里接单
  • 白城网站建设公司软件外包开发
  • 广元网站设计织梦cms 官方网站
  • 如何把网站做权重pageadmin和wordpress
  • 用dedecms做的网站 脚本是什么网页设计师属于什么部门
  • 快站优惠券去哪里找WordPress加载语言包
  • 网站排名优化原理织梦做的网站后台登录
  • 苏州网络科技公司建网站看优秀摄影做品的网站
  • 台州云建站模板wordpress的搜索结果
  • 在网盘上怎么做自己的网站宁波网站建设免费咨询
  • 男女做爰视频网站手机html网页制作软件
  • 做造价在那个网站比较好关于网站规划建设方案书
  • 佛山网站建设全方位服务全椒网站建设
  • 松江品划企业网站建设利用ionic做的网站
  • 网站放在服务器上手机房屋3d设计软件
  • 一流的嘉兴网站建设中国十大权威新闻媒体
  • 服务提供网站粤icp备案号查询网官网
  • 怎么创建网站域名品牌vi标志设计公司
  • 烟台福山网站建设营销型网站建设的指导原则不包括