Python笔记(六)

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>

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值