1. 上下文管理器
1.1 定义
一个类,实现了__enter__()方法和__exit()__方法,通过该类创建的对象就是上下文管理器。
1.2 __enter__方法
__enter__方法是上文方法,需要返回一个文件对象
1.3 __exit__方法
__exit__方法是下文方法,with语句执行完,会自动执行,即使出现异常也会执行。
1.4 自定义上下文管理器
with 可以自动关闭文件对象,无论是否异常都会关闭文件对象,with语句操作建立在上下文管理器的基础上。
class MyContextManager:
def __init__(self, file_name, model):
self.file_name = file_name
self.model = model
def __enter__(self):
print("上文方法")
self.f = open(file=self.file_name, mode=self.model)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
print("下文方法")
self.f.close()
with MyContextManager("./static/404.html", model="rb") as m:
m.read()
2. 生成器
2.1 什么是生成器
生成器是根据算法生成数据的一种机制,每次调用生成器只生成一个值,可以节省大量内存。
使用生成器可以用多少数据就生成多少数据,可以减少内存的使用。
2.2 生成器推导式
列表推导式:data_list = [x for x in range(10)]
生成器推导式: data_gen = (x for x in range(10))
使用next(data_gen)
获取生成器里面的数据,但是一次只能获取一个值。
2.3 yield关键字
在def函数中具有yield关键字就是生成器
def my_generator():
for i in range(10):
yield i
g = my_generator()
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 2
2.4 斐波那契数列
斐波那契数列数列特点:数列中第一个数为0,第二个数为1,其后的每一个数都是前两个数相加的和。
实现斐波那契数列,传入展示的位数,输出对应的数列。
def Fibonacci(num):
first = 0
second = 1
index = 0
while num > index:
if num == 1 or index == 0:
index += 1
yield first
elif num == 2 or index == 1:
index += 1
yield second
else:
first, second = second, first + second
index += 1
yield second
fb = Fibonacci(5)
print(next(fb))
print(next(fb))
print(next(fb))
print(next(fb))
print(next(fb))
3. 浅拷贝和深拷贝
3.1 可变数据类型/不可变数据类型
可变数据类型有:列表、字典、集合
不可变数据类型有:整形、浮点型、字符串、元组
3.2 浅拷贝
在python中,copy函数是浅拷贝,只对可变类型的第一层对象进行拷贝。对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象。
3.2.1 可变类型的浅拷贝
对于可变类型的浅拷贝,只是新开辟了一块内存空间进行存储,对于内部的子对象并没有拷贝。
代码验证
a = [1, 2, 3]
b = [4, 5, 6]
data = [a, b]
data_copy = copy.copy(data)
data1 = data
# 验证拷贝是否新开辟了一块空间
print(id(data))
print(id(data1)) # 引用的data的地址空间
print(id(data_copy)) # 新开辟的地址空间
# 验证子对象的地址是否一致
print(id(data[0]))
print(id(data_copy[0]))
执行结果
3.2.2 不可变类型的浅拷贝
对于不可变类型,浅拷贝不会开辟新空间,只是拷贝了引用。
# 不可变类型
my_tuple1 = (1, 2, 3)
my_tuple2 = (4, 5, 6)
my_tuple = (my_tuple1, my_tuple2)
my_copy_tuple = copy.copy(my_tuple)
print(id(my_tuple))
print(id(my_copy_tuple))
3.3 深拷贝
在python中深拷贝使用deepcopy函数实现。深拷贝会对每一层拷贝的对象都会开辟新的内存空间存储。
3.3.1 可变类型的深拷贝
深拷贝,不仅会把第一层对象单独开辟一个空间,还会把每层的对象都单独开辟一个空间。
代码验证:
import copy
# 可变类型
a = [1, 2, 3]
b = [4, 5, 6]
data = [a, b]
data_copy = copy.copy(data)
data1 = data
data2 = copy.deepcopy(data)
# 验证拷贝是否新开辟了一块空间
print(f"原数据:{id(data)}")
print(f"普通赋值{id(data1)}") # 引用的data的地址空间
print(f"浅拷贝:{id(data_copy)}") # 新开辟的地址空间
print(f"深拷贝:{id(data2)}") # 新开辟的地址空间
# 验证子对象的地址是否一致
print(f"原数据的子对象:{id(data[0])}")
print(f"浅拷贝的子对象:{id(data_copy[0])}")
print(f"深拷贝的子对象:{id(data2[0])}") # 新开辟地址空间
3.3.2 不可变类型的深拷贝
对于不可变类型的数据,深拷贝也是拷贝了引用,没有开辟新的地址空间。
# 不可变类型
my_tuple1 = (1, 2, 3)
my_tuple2 = (4, 5, 6)
my_tuple = (my_tuple1, my_tuple2)
my_copy_tuple = copy.copy(my_tuple)
my_deepcopy_tuple = copy.deepcopy(my_tuple)
print(id(my_tuple))
print(id(my_copy_tuple))
print(id(my_deepcopy_tuple))
4. 正则表达式
正则表达式是记录文本规则的代码,是为了匹配或者查找某些规则的字符串
在Python中需要导入re模块来使用。
使用re.match(正则表达式,要匹配的字符串)来进行匹配,然后对结果使用group()方法来提取数据。
4.1 匹配单个字符
符号 | 含义 |
---|---|
. | 匹配任意一个字符,除了\n |
[] | 匹配[]中列举的字符 |
\d | 匹配数字[0-9] |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即空格和tab |
\S | 匹配非空白 |
\w | 匹配非特殊字符,即字母、数字、汉字、_ |
\W | 匹配特殊字符,即非字母、非汉字、非数字、非_ |
# . 匹配任意一个字符,除了\n
print(re.match("ceyye.", "ceyyen").group()) # 能匹配到,结果是ceyyen
print(re.match("ceyye.", "ceyye").group()) # 不能匹配到
print(re.match("ceyye.", "ceyye\n").group()) # 不能匹配到
# [] 匹配[]中列举的字符
print(re.match("ceyye[abcn]", "ceyyen").group()) # 能匹配到,结果是ceyyen
print(re.match("ceyye[abcn]", "ceyyed").group()) # 不能匹配到
# \d 匹配数字[0-9]
print(re.match("1234567\d", "12345678").group()) # 能匹配到,结果是12345678
print(re.match("1234567\d", "1234567hhh").group()) # 不能匹配到
print(re.match("1234567\d", "12345678hhh").group()) # 能匹配到,结果是12345678
# \D 匹配非数字,即不是数字
print(re.match("1234567\D", "1234567hhh").group()) # 能匹配到,结果是1234567h
# \s 匹配空白,即空格和tab
print(re.match("123\s", "123 456 7hhh").group()) # 能匹配到,结果是123
print(re.match("123\s", "123 4567hhh").group()) # 能匹配到,结果是123
# \S 匹配非空白
print(re.match("123\S", "1234567hhh").group()) # 能匹配到,结果是1234
# \w 匹配非特殊字符,即字母、数字、汉字、_
print(re.match("123\w", "123_22").group()) # 能匹配到,结果是123_
# \W 匹配特殊字符,即非字母、非汉字、非数字、非_
print(re.match("123\W", "123>22").group()) # 能匹配到,结果是123>
4.2 匹配多个字符
符号 | 含义 |
---|---|
* | 匹配前一个字符出现0次或者无限次 |
+ | 匹配前一个字符出现1次或者无限次 |
? | 匹配前1个字符出现0次或1次 |
{m} | 匹配前1个字符出现m次 |
{m,n} | 匹配前1个字符出现从m到n次 |
# * 匹配前一个字符出现0次或者无限次
print(re.match("123456*", "12345").group()) # 能匹配到,结果是12345
print(re.match("123456*", "12345666666").group()) # 能匹配到,结果是12345666666
# + 匹配前一个字符出现1次或者无限次
print(re.match("123456+", "12345666666").group()) # 能匹配到,结果是12345666666
print(re.match("123456+", "123456").group()) # 能匹配到,结果是123456
# ?匹配前1个字符出现0次或1次
print(re.match("123456?", "12345").group()) # 能匹配到,结果是12345
print(re.match("123456?", "123456").group()) # 能匹配到,结果是123456
# {m} 匹配前1个字符出现m次
print(re.match("123456{3}", "12345666").group()) # 能匹配到,结果是12345666
# {m,n} 匹配前1个字符出现从m到n次
print(re.match("123456{0,3}", "12345666").group()) # 能匹配到,结果是12345666
print(re.match("123456{0,3}", "12345").group()) # 能匹配到,结果是12345
4.3 匹配开头和结尾
符号 | 含义 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
[^指定字符] | 匹配除了指定字符以外的所有字符 |
# 匹配开头和结尾
# ^ 匹配字符串开头
print(re.match("^ceyyen", "ceyyen").group()) # 能匹配到,结果是ceyyen
print(re.match("^ceyyen", "11ceyyen").group()) # 不能匹配到
print(re.match("^ceyyen", "mceyyen").group()) # 不能匹配到
# $ 匹配字符串结尾
print(re.match("ceyyen$", "ceyyen").group()) # 能匹配到,结果是ceyyen
print(re.match("ceyyen$", "ceyyen12").group()) # 不能匹配到
print(re.match("ceyyen$", "ceyyenm").group()) # 不能匹配到
# [^指定字符] 匹配除了指定字符以外的所有字符
print(re.match("cey[^a]yen", "ceyyen").group()) # 不能匹配到
print(re.match("cey[^a]yen", "ceybyen").group()) # 能匹配到,结果是ceybyen
print(re.match("cey[^a]yen", "ceyayen").group()) # 不能匹配到
4.4 匹配分组
符号 | 含义 |
---|---|
| | 匹配左右任意一个表达式 |
(a,b) | 将括号中字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?p<name>) | 分组起别名 |
(?p=name) | 引用别名为name分组匹配到的字符串 |
# 匹配分组
# | 匹配左右任意一个表达式
print(re.match("ceyyen@(163|126|qq).com", "ceyyen@163.com").group()) # # 能匹配到,结果是ceyyen@163.com
print(re.match("ceyyen@(163|126|qq).com", "ceyyen@qq.com").group()) # # 能匹配到,结果是ceyyen@qq.com
# (a,b) 将括号中字符作为一个分组
print(re.match("ceyyen@(163|126|qq).com", "ceyyen@126.com").group()) # # 能匹配到,结果是ceyyen@126.com
# num 引用分组num匹配到的字符串 group(0):匹配所有数据 group(1):匹配第一个分组的数据 group(2):匹配第二个分组的数据
print(re.match("ceyyen@(163|126|qq).com", "ceyyen@126.com").group(0)) # # 能匹配到,结果是ceyyen@126.com
print(re.match("ceyyen@(163|126|qq).com", "ceyyen@qq.com").group(1)) # # 能匹配到,结果是qq
# print(re.match("ceyyen@(163|126|qq).com", "ceyyen@qq.com").group(2)) # # 不能匹配到,因为只有一个分组
# (?P<name>) 分组起别名
print(re.match("ceyyen@(?P<name>163|126|qq).com", "ceyyen@qq.com").group()) # 能匹配到,结果是ceyyen@qq.com
# (?P=name) 引用别名为name分组匹配到的字符串,如果name分组获取到的是html,那么引用name分组时,也会校验是否是html
print(re.match("<(?P<html>[a-zA-Z]{4})><(?P=html)>", "<html><head></head></html>").group()) # 不能匹配到
print(re.match("<(?P<html>[a-zA-Z]{4})><(?P=html)>", "<html><html></head></html>").group()) # 能匹配到,结果是<html><html>