python3基础2026.7.28

📅 2026/7/29 23:05:15
python3基础2026.7.28
Python生成器生成器根据程序设计者制定的规则循环生成数据当条件不成立时则生成数据结束。数据不是一次性全部生成出来而是使用一个再生成一个可以节约大量的内存创建生成器的方法生成器的推导式生成器推导式 (生成器推导式 数据的生成规则)创建生成器my_generator (i * 2 for i in range(5)) print(my_generator)my_generator 本质是一个对象其内部并没有具体的数据只有数据的生成规则运行验证如下next获取生成器下一个值生成器中有一个关键的函数 next 每调用一次其就会根据生成器的数据生成规则创建一个元素。然后向下移动再次调用next函数则根据生成器的数据生成规则在创建一个元素#next获取生成器下一个值 value next(my_generator) print(value)遍历生成器for value in my_generator : print(value)输出结果生成器相关函数next函数获取生成器中的下一个值for循环遍历生成器中的每一个值总结1.生成器中并没有真正的保存具体的数据其返回结果是一个对象对象中保存了所有数据的生成规则2.生成器中有一个特别重要的函数next每调用一次next则自动生成器中根据生成规则获取一个元素3.生成器的意义降低程序的能耗因为不需要存储大量的数据只是在调用next时才生成一个元素所有能耗大大降低生成器作用案例1.使用列表存储一组数据数据一共有1000万个要求存储时每个列表元素是循环次数的平方: 生成器可以降低程序的能耗加快程序的执行时间 #导入time模块 import time #获取当前程序开始的时间 start time.time() #使用列表存储一组数据数据一共有100万个要求存储时每个列表元素是循环次数的平方 square_nums [i * i for i in range(10000000)] #获取程序结束前的时间 end time.time() #计算程序执行的总时间 print(f程序执行时间{end - start})执行时间2.使用列表存储一组数据数据一共有1000万个要求存储时每个列表元素是循环次数的平方查看内存使用:列表推导式import tracemalloc tracemalloc.start() data [i for i in range(1000000)] # data (i * i for i in range(1000000)) snapshot tracemalloc.take_snapshot() top_stats snapshot.statistics(lineno) for stat in top_stats[:10]: print(stat)输出结果生成器推导式import tracemalloc tracemalloc.start() data (i * i for i in range(1000000)) # 真正消费生成器 res list(data) snapshot tracemalloc.take_snapshot() top_stats snapshot.statistics(lineno) for stat in top_stats[:10]: print(stat)输出结果推到总结1、内存占用核心差异列表推导式[]程序运行瞬间把 100 万个元素全部存入内存需要开辟一块连续内存保存全部数值tracemalloc 会观测到巨大内存占用优点可以随机索引data[5000]、重复遍历缺点数据量大时极易造成内存暴涨、OOM。生成器表达式()惰性计算懒加载不会一次性生成所有元素仅保存迭代逻辑、当前状态不会预存全部结果只有调用next()/for 循环迭代时才临时算出单个值用完就丢弃tracemalloc 观测到内存占用极低限制只能单向遍历一次不支持下标随机访问。2、两份代码观测结果说明第 1 段列表推导式输出会看到明显很大的内存占用行数 第 2 段生成器输出几乎看不到高额内存占用yield生成器在def函数中具有yield关键字yield生成器其结构一共分为两部分 1.首先要定义一个函数 2.在函数内部存在一个yield关键字 所以我们把1.2组合在一起就称之为yield生成器 另外要特别强调yield生成器是一个对象而不是一个函数 重点理解yield生成器的执行流程其使用方式和意义和刚才介绍的一模一样def generator(n): for i in range(n): print(开始生成...) yield i print(完成一次...) #使用时由于生成器需要传递参数所以通常将其赋予给某个变量 g generator(5) print(next(g)) print(next(g)) print(next(g)) print(next(g)) print(next(g))输出结果问题现象现象分析遇见 yield → 暂停返回数据下次 next 从暂停位置继续往下执行暂时把yield当做return理解每次遇到yield生成器就相当于执行1次调用generator(5)不会执行函数代码只是创建生成器对象如果继续调用第 6 次next(g)循环结束没有下一个 yield直接抛出StopIteration异常和普通 return 区别return直接终止函数无法恢复yield暂停函数保留上下文可以再次恢复运行用yield生成斐波那契数列数学中有个著名的斐波那契数列要求数列中第一个数为1第二个数也为1其后每个数都可由前两个数相加得到例子1.2.2.3.5.8...现在我们使用生成器来实现这个斐波那契数列每次取值都通过算法来生成下一个数据生成器每次调用只生成一个数据可以节省大量的内存。def fib(max): n,a,b 0,0,1 while n max: yield b a,b b,ab n 1 for n in fib(5): print(n)算法分析输出结果代码分析fib(5)创建生成器对象不执行函数内部代码进入 for 循环迭代生成器每次迭代触发一次next()第 1 次yield b1→ 输出 1暂停赋值迭代a1b011n1第 2 次yield b1→ 输出 1暂停赋值迭代a1b112n2第 3 次yield b2→ 输出 2暂停赋值迭代a2b123n3第 4 次yield b3→ 输出 3暂停赋值迭代a3b235n4第 5 次yield b5→ 输出 5暂停n5不满足循环条件生成器遍历结束结果分析生成器特性yield暂停函数、返回数据下次迭代从暂停处继续执行优势不用一次性生成完整数列节省内存适合海量数据迭代for循环可以直接遍历生成器自动调用next()结束后自动捕获StopIteration异常不报错python中的深浅拷贝基础概念1.变量是一个系统表的元素拥有指向对象的连接空间2.对象被分配的一块内存储存其所代表的值3.引用是自动形成的从变量到对象的指针4.类型属于对象而非变量5.不可变对象一旦创建就不可修改的对象包括字符串、元组、数值类型该对象所指向的内存中的值不能被改变。当改变某个变量时候由于其所指的值不能被改变相当于把原来的值复制一份后再改变这会开辟一个新的地址变量在指向这个新地址6.可变对象可以修改的对象包括列表、字典、集合该对象所指向的内存中的值可以被改变。变量准确的说是引用改变后实际上是其所指的值直接发送改变并没有发送复制行为也没有开辟新的地址通俗点说就是原地改变当我们写a pythonpython解释器干的事情1.创建变量a2.创建一个对象分配一块内存来存储值python3.将变量与对象通过指针连接起来从变量到对象的连接称为引用变量引用对象通常来说 变量a 存储的“python”的地址方法证明#定义一个变量A a python #python中存在一个id方法可以用于获取变量指向内存空间 print(id(a))数据结果扩展可变数据类型(值内存空间一旦固定其值是可以发生改变列表、字典、集合)#扩展可变数据类型 a [1,3,5] print(id(a)) a.append(7) print(id(a))输出结果可变是指值可以改变但是a中存储的地址却为不变。扩展不可变数据类型值内存空间一旦固定其值是不可以发生改变数值、布尔、字符串、元组b 10 print(id(b)) b hello print(id(b))输出结果代码分析b 10Python 在内存创建整数对象 10变量b引用这个对象 执行print(id(b))→ 输出第一个内存地址1683870556。b hello⚠️不是修改原来 10 这个对象Python 在内存新建一个字符串对象 hello然后让变量b重新指向这个新对象。 原先数字10的对象还存在内存中没有变量引用后续会被垃圾回收。执行print(id(b))→ 输出新地址72460480。两次打印 id 结果不一样证明变量 b 指向了两个完全不同的内存对象。赋值赋值只是复制了新对象的引用不会开辟新的内存空间。并不会产生一个独立的对象单独存在只是将原有的数据块打上一个新标签所以当其中一个标签被改变的时候数据块就会发生变化另一个标签也会随之改变。#定义一个变量a a [1,3,5] print(id(a)) #把变量a赋值给变量B b a print(id(b))输出结果变量a与变量b都指向同一个地址浅拷贝浅拷贝创建新对象其内容是原对象的引用。浅拷贝之所以称为浅拷贝是它仅仅只拷贝了一层拷贝了最外围的对象本身内部的元素都只是拷贝了一个引用而已案例1赋值案例2可变类型浅拷贝可变类型的浅拷贝概念浅拷贝Shallow Copy指创建一个新对象开辟一块新空间然后值拷贝。但新对象内部的子对象仍引用原始对象中的子对象。对于可变类型如列表、字典浅拷贝仅复制最外层的结构嵌套的可变对象不会被递归复制。实现浅拷贝的方法方法1使用copy()方法适用于列表、字典等可变类型直接调用内置的copy()方法生成浅拷贝。original_list [1, [2, 3], 4] shallow_copy original_list.copy()类似于方法2使用list()或dict()构造函数通过类型构造函数生成新对象但嵌套对象仍共享引用。original_dict {a: [1, 2], b: 3} shallow_copy dict(original_dict)方法3使用切片操作仅限列表对列表进行全切片[:]可生成浅拷贝。original_list [1, [2, 3], 4] shallow_copy original_list[:]浅拷贝的特性验证修改浅拷贝的最外层元素不会影响原对象但修改嵌套的可变对象会同步影响原对象。original_list [1, [2, 3], 4] shallow_copy original_list.copy() shallow_copy[0] 99 # 仅修改浅拷贝的外层 print(original_list) # 输出 [1, [2, 3], 4]原对象未变 shallow_copy[1][0] 88 # 修改嵌套的可变对象 print(original_list) # 输出 [1, [88, 3], 4]原对象同步变化案例3不可变类型浅拷贝不可变类型的浅拷贝在Python中不可变类型如整数、字符串、元组等的浅拷贝行为与可变类型如列表、字典等有所不同。不可变类型的浅拷贝通常不会创建新的对象而是直接引用原始对象。不可变类型的浅拷贝特点不可变对象在内存中一旦创建就无法修改。进行浅拷贝时Python会直接引用原始对象而不是创建新对象。因为不可变对象无法修改所以即使多个变量引用同一个对象也不会产生副作用。示例代码a 10 # 整数是不可变类型 b a # 浅拷贝实际上是引用同一个对象 print(id(a) id(b)) # 输出True说明a和b引用的是同一个对象 a 20 # 重新赋值此时a指向新对象 print(a, b) # 输出20 10b仍然指向原来的对象字符串的浅拷贝s1 hello s2 s1 # 浅拷贝 print(id(s1) id(s2)) # 输出True s1 world # s1指向新对象 print(s1, s2) # 输出world hello元组的浅拷贝t1 (1, 2, 3) t2 t1 # 浅拷贝 print(id(t1) id(t2)) # 输出True为什么不需要深拷贝不可变类型由于不可变对象无法被修改深拷贝对于不可变类型来说没有实际意义。无论是浅拷贝还是深拷贝结果都是引用同一个对象。因此对于不可变类型通常不需要考虑拷贝的问题。总结不可变类型的浅拷贝只是创建对原始对象的新引用。修改其中一个变量实际上是重新赋值不会影响其他变量。在Python中不可变类型的拷贝操作是高效且安全的。深拷贝深拷贝和浅拷贝对应深拷贝拷贝了对象的所以元素包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象不再与原来的对象有任何关联。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。只有一种形式copy模块中的deepcopy函数。可变类型深拷贝代码#定义一个变量 import copy a [1,3,5,[2.5]] #定义一个变量b,对变量a进行深拷贝 b copy.deepcopy(a) print(a) print(b) print(id(a)) print(id(b))输出结果结论1.对于简单的可变数据类型深拷贝可以对对象进行完全拷贝生成一块独立的内存空间两个变量之间没有任何关系2.对于复杂的可变类型数据深拷贝可以对对象进行完全拷贝不仅可以拷贝外层对象也可以拷贝内存对象而且完全独立。不可变类型深拷贝不可变类型进行深拷贝不会给拷贝的对象开辟新的内存空间而只是拷贝内存地址简单的不可变类型数据代码示例#定义一个变量 import copy a (1,2,3) #定义一个变量b,对变量a进行深拷贝 b copy.deepcopy(a) print(a) print(b) print(id(a)) print(id(b))输出结果结论对于简单的不可变类型数据深拷贝也只能拷贝对象的引用关系所以看到的结果是a和b指向了相同的内存空间复杂的不可变类型数据代码#定义一个变量 import copy a (1,2,3,(4.8)) #定义一个变量b,对变量a进行深拷贝 b copy.deepcopy(a) print(a) print(b) print(id(a)) print(id(b))输出结果结论对于复杂的不可变类型数据深拷贝也只能拷贝对象的引用关系所以看到的结果是a和b指向了相同的内存空间深浅拷贝的特殊案例1.python中深浅拷贝特殊案例可变嵌套不可变类型外层拷贝代码import copy a [1,3,5,(4,7)] b copy.copy(a) c copy.deepcopy(a) print(a) print(b) print(c) print(id(a)) print(id(b)) print(id(c))输出结果内层拷贝代码import copy a [1,3,5,(4,7)] b copy.copy(a) c copy.deepcopy(a) print(a) print(b) print(c) print(id(a[3])) print(id(b[3])) print(id(c[3]))‘输出结果结果分析外层是可变类型所以可以进行完全拷贝需要生成内存空间但是内层对象是不可变数据类型所以智能拷贝引用关系2.python中深浅拷贝特殊案例不可变嵌套可变类型外层拷贝代码import copy d (1,3,5,[7,9]) e copy.copy(d) f copy.deepcopy(d) print(外层结构) print(id(d)) print(id(e)) print(id(f)) print(内层结构) print(id(d[3])) print(id(e[3])) print(id(f[3]))输出结果特殊结论如果是一个不可变数据类型包含了可变数据类型浅拷贝结论与之前结论一致都只能拷贝引用关系但是对于深拷贝这个有点不同了如果这种类型使用深拷贝其整体都可以进行完全拷贝特殊外层是不可变内层是可变结果整体都可以进行完全拷贝结果如下图所示正则表达式在实际开发过程中经常会由查找符合某些复杂规则的字符串的需要比如邮箱图片地址手机号码等这时候想匹配或者查找符合某些规则的字符串就可以使用正则表达式了概述正则表达式就是字符串的匹配规则结果匹配满足正则条件的字符串正则表达式描述了一种字符串匹配的模式可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。模式一种特定的字符串模式这个模式是通过一些特殊的符合组成的某种也可以理解为是一种模糊匹配。精准匹配select * from blog where title python模糊匹配select * from blog where title like %python%正则表达式并不是python所特有的在其他语言中都支持正则表达式的。正则表达式的功能正则表达式的核心功能数据验证正则表达式常用于表单字段的格式验证确保用户输入符合特定模式。例如验证手机号、邮箱、身份证号或IP地址的合法性。通过预定义规则快速判断输入是否有效减少后端处理无效数据的压力。手机号验证中国大陆^1[3-9]\d{9}$邮箱验证基础版^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}$数据检索在文本处理中快速定位符合特定模式的字符串适用于日志分析、文本编辑或爬虫数据提取。支持复杂匹配规则如多条件组合、模糊匹配等。提取HTML中的链接a\shref([^])匹配日期格式YYYY-MM-DD\d{4}-\d{2}-\d{2}数据脱敏对敏感信息进行部分隐藏平衡数据可用性与隐私保护。常用于显示姓名、联系方式等场景。手机号脱敏保留前3后4位(\d{3})\d{4}(\d{4}) 替换为$1****$2姓名脱敏中文姓名保留首字([\u4e00-\u9fa5])([\u4e00-\u9fa5]) 替换为$1**数据过滤识别并处理文本中的敏感词或非法内容常用于论坛、聊天系统的内容审核。通过模式匹配实现批量过滤。基础关键词过滤(敏感词A|敏感词B|违规词) 替换为***高级过滤忽略大小写和部分变形(?i)s[!#$%^*]*e[!#$%^*]*x实现要点验证场景需严格限定字符串起止^和$检索场景优先使用非贪婪匹配.*?脱敏场景注意分组捕获与反向引用过滤场景考虑性能优化如预编译正则re模块的介绍1.什么是re模块在python中需要通过正则表达式对字符串进行匹配的时候可以使用一个re模块2.re模块使用三步走注意第二步:match专门用于匹配以某些字符/字符串开头的内容只能匹配开头findall(目标要查找的内容):匹配以某些字符/字符串开头的内容第三步如果数据匹配失败则返回None正则表达式快速入门案例1查找一个字符串中是否具有数字“8”import re result re.findall(8,136526856541) if result: print(匹配成功) else: print(匹配失败)输出结果案例2查找一个字符串是否具有数字(\d代表数字)import re result re.findall(\d,13aadsda6526856541) print(result)输出结果案例3查找一个字符串中所有的非数字字符\D代表非数字import re str assvsds5#%%^ result re.findall(\D,str) print(result)输出结果正则表达式的编写三步走查什么查多少从哪查1.查什么字符簇常见写法① [abcdefg] 代表匹配abcdefg字符中的任意某个字符1个② [aeiou] 代表匹配a、e、i、o、u五个字符中的任意某个字符③ [a-z] 代表匹配a-z之间26个字符中的任意某个④ [A-Z] 代表匹配A-Z之间26个字符中的任意某个⑤ [0-9] 代表匹配0-9之间10个字符中的任意某个import re str1 0sdasfa125 result re.match([0-9],str1) print(result.group())输出结果⑥ [0-9a-zA-Z] 代表匹配0-9之间、a-z之间、A-Z之间的任意某个字符字符簇托字节结合代表取反的含有① [^aeiou] 代表匹配除了 a、e、i、o、u 以外的任意某个字符② [^a-z] 代表匹配除了 a-z 以外的任意某个字符\d等价于[0-9],代表匹配0-9之间的任意字符\D等价于[0-9],代表匹配0-9之外的任意字符代码示例str3 $123545 result re.match([^0-9],str3) print(result.group())输出结果2.查多少基本语法正则匹配字符.或者\w或\s跟查多少如\w{68}如.*匹配前面的字符出现0次或者多次代码示例import re #定义一个字符串 str1 123abc # 用于匹配连续的三个数 pattern r\d{3} result1 re.search(pattern,str1) print(result1.group()) # 用于匹配多个字符最少匹配0个最多匹配n个 str2 srcds25858,jpg result2 re.match(.*,str2) print(result2.group()) #匹配1或0 str3 1 result3 re.match(10?,str3) print(result3.group())结果3. 从哪查1.^匹配字符串开头示例示例 1校验字符串是否以数字开头import re pattern r^\d # 以数字开头 print(re.match(pattern, 123abc)) # 匹配成功匹配到 1 print(re.match(pattern, abc123)) # 匹配失败返回 None示例 2匹配以http开头的网址import re pattern r\d$ print(re.search(pattern, 商品编号886)) # 匹配成功匹配到 6import re url https://www.baidu.com result re.match(r^https?, url) if result: print(是http/https开头的网址, result.group())2.$匹配字符串结尾示例示例 1校验域名是否以.com结尾import re pattern r\.com$ print(re.search(pattern, www.baidu.com)) # 匹配成功 print(re.search(pattern, www.baidu.cn)) # 匹配失败示例 2校验字符串是否以数字结尾import re pattern r\d$ print(re.search(pattern, 商品编号886)) # 匹配成功匹配到 63.、^$组合使用最常用全字符串校验同时限定开头和结尾要求整个字符串完全符合规则是表单校验的标准写法示例 校验 6~8 位纯数字密码import re pattern r^\d{6,8}$ print(re.match(pattern, 123456)) # 成功恰好6位数字 print(re.match(pattern, 12345)) # 失败不足6位 print(re.match(pattern, 123456789)) # 失败超过8位 print(re.match(pattern, 123a56)) # 失败包含非数字字符核心作用加了^$后字符串必须从头到尾完全匹配规则多一个字符、少一个字符都不通过。