370-Python高级语法

  1. with语句:上下文管理器
    • 上文管理器:open
    • 下文管理器:close
  2. yield生成器(掌握两种创建方式)
    • 类似推导式结构
    • 大大降低程序能耗
  3. 深浅拷贝
    • 面试考的多:深拷贝和浅拷贝的区别
  4. 正则表达式

写在前面

老的文件操作方法:

# 写入模式打开文件
f = open("1.txt", "w")
# 写入文件内容
f.write("Ikun")
# 关闭文件
f.close()

文件在使用完后必须关闭,因为文件对象会占用操作系统资源,并且操作系统同一时间能打开的文件数量也是有限的(linux内核信息定义的最大打开文件数是1024)

老方法存在的问题:

#1、以读的方式打开文件
f = open("1.txt", "r")  # 只读模式
#2、写入文件内容
f.write("Ikun")  # 但是写入文件
#3、关闭文件
f.close()

由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用,这里就会造成资源浪费(会占用系统资源)

对此问题,可以用try...except...语句来解决

但是最好的方案是使用with语句(高级语法,代码简化)

with语句和上下文管理器

上下文管理器和with语句的定义

  • 上下文管理器:上下文管理器是Python中的一个协议,用于管理资源的生命周期,例如文件的打开和关闭、数据库的连接和断开等
  • with语句:with语句用于管理上下文管理器,在文件操作中,with语句可以自动管理文件的打开和关闭,避免忘记关闭文件导致资源泄露

Python提供了 with 语句的写法,既简单又安全。

文件操作的时候使用with语句可以自动调用关闭文件操作,即使出现异常也会自动关闭文件操作。

使用with方法实现文件操作,如下所示:

# 1、以写的方式打开文件
with open('1.txt', 'w') as f:
    # 2、读取文件内容
    f.write('Ikun')

上下文管理器

当使用with语句后,就会自动创建上文管理器(__enter()__)和下文管理器(__exit()__)

__enter()__f = open("1.txt", "w")
__exit()__f.close()

生成器是什么

根据程序设计者制定的规则循环生成数据,当条件不成立时则生成数据结束

作用

数据不是一次性全部生成出来,而是使用一个,再生成一个,可以节约大量的内存,降低程序运行消耗时间

创建生成器的方式

推导式 生成器
yield 关键字生成器

推导式生成器

列表推导式类似,只不过生成器推导式使用小括号。
109-推导式(数据容器)

# 创建生成器
my_generator = (i * 2 for i in range(5))
print(my_generator)

(i * 2 for i in range(5))是生成器
[i * 2 for i in range(5)]是直接生成现成的列表

(i * 2 for i in range(5))本质是一个对象,其中没有02468这些数据,只有数据生成的规则
如果打印my_generator,那么打印的只是这个对象的内存地址

# next 获取生成器下一个值
value = next(my_generator)
print(value)
  • 使用next()函数获取生成器的元素:
    生成器中有一个关键的函数:next(),每调用一次就会根据规则创建一个元素,然后向后移动,再次调用next()函数就会再生成一个元素
# 遍历生成器
for value in my_generator:
    print(value)
  • for 循环遍历生成器中的每一个值

yield生成器

yield生成器是啥

重点理解yield生成器的执行流程

yield生成器结构:

  1. 定义一个函数
  2. 函数内部存在一个yield关键字
    yield 关键字生成器的特征:在def函数中具有yield关键字

当函数和yield组合在一起就叫做生成器,这个函数就不是函数了,就转变成了对象

代码演示:

用next函数获取生成器的元素

def generator(n):
    for i in range(n):
        print('开始生成数据')
        yield i  # 暂时可以把yield当成return理解,每次遇到yield,生成器就相当于执行一次(生成循环执行到yield后就暂停,后面在调用就从这里继续执行,意思就是'完成一次数据生成'这句话会在第二次生成的时候打印)
        print('完成一次数据生成')

# 使用时,由于生成器需要传递参数,所以通常将其赋予给某个变量
g = generator(5)
# 如果print(generator(5)),输出的是对象的内存地址
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))				----->    正常
print(next(g))  			----->    报错
Traceback (most recent call last):
  File "/Users/cndws/PycharmProjects/pythonProject/demo.py", line 14, in <module>
    print(next(g))
StopIteration(到达边界)
但是这里执行第六次next()会把'完成一次数据生成'这句打印出来

用for循环遍历生成器的元素

for循环的好处:可以避免上面出现的到达边界的错误

def generator(n):
    for i in range(n):
        print('开始生成...')
        yield i
        print('完成一次...')
        
g = generator(5)
for i in g:
    print(i)

使用while循环+try...except语句获取生成器元素

这样同样可以避免到达边界的错误

def generator(n):
    for i in range(n):
        print('开始生成...')
        yield i
        print('完成一次...')
        
g = generator(5)
while True:
    try:
        print(next(g))
    except StopIteration:
        break

yield关键字和return关键字

如果不太好理解yield,可以先把yield当作return的同胞兄弟来看,他们都在函数中使用,并履行着返回某种结果的职责。

这两者的区别是:

return的函数直接返回所有结果,程序终止不再运行,并销毁局部变量;

def example():
    x = 1
    return x

example = example()
print(example)

而有yield的函数则返回一个可迭代的 generator(生成器)对象,你可以使用for循环或者调用next()方法遍历生成器对象来提取结果。

def example():
    x = 1
    y = 10
    while x < y:
        yield x
        x += 1

example = example()
print(example)

生成器的现实应用案例

斐波那契数列:数学中的一个概念,是一组有规律的数字!
1 1 2 3 5 8 13 21 34 55 ...
规律:
隐藏了一个元素 => 0
第1个元素为1
第2个元素为1,第2个元素为1,实际上是由0 + 1 = 隐藏元素 + 第一个元素
第3个元素为2 = 1 + 1,第3个元素值 = 第2个元素值 + 第1个元素值
第4个元素为3 = 2 + 1,第4个元素值 = 第3个元素值 + 第2个元素值
刚好满足:数据量比较大,而且还要经过大量的计算 => 符合生成器的使用规则
0 1 1 2 3 5 8 13 21 34 55 ...
a变量代表,斐波那契数列中前一个元素,比如a可以代表0
b变量代表,斐波那契数列中后一个元素,比如b可以代表1
a,b是相邻的两个元素

def fib(max):  # 求max位斐波那契数列 => 前max位斐波那契数列的每个值
    n, a, b = 0, 0, 1
    while n < max:
        yield b  # 第一次循环,弹出一个1
        # 想个办法,让第二次yield b,弹出一个1,第三次yield b,弹出一个2
        # a = 0, b = 1
        a, b = b, a + b
        # a = 1, b = 1
        n += 1

result = fib(50)
for i in result:
    print(i)

使用生成器的注意事项:

① 代码执行到 yield 会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行

② 生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常

③ while 循环内部没有处理异常操作,需要手动添加处理异常操作

④ for 循环内部自动处理了停止迭代异常,使用起来更加方便,推荐大家使用。