【博学谷学习记录】超强总结,用心分享|人工智能第二十二课Python中高级语法!

一、Python高级语法

1、with语句和上下文管理器

☆ with语句

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

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

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

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

2、生成器的创建方式

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

数据不是一次性全部生成出来,而是使用一个,再生成一个,可以节约大量的内存。

创建生成器的方式

① 生成器推导式

② yield 关键字

☆ 生成器推导式

与列表推导式类似,只不过生成器推导式使用小括号。

# 创建生成器
my_generator = (i * 2 for i in range(5))
print(my_generator)
​
# next获取生成器下一个值
# value = next(my_generator)
# print(value)
​
# 遍历生成器
for value in my_generator:
    print(value)

生成器相关函数:

next 函数获取生成器中的下一个值
for  循环遍历生成器中的每一个值 

☆ yield生成器

yield 关键字生成器的特征:在def函数中具有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))              ----->    正常
print(next(g))              ----->    报错
Traceback (most recent call last):
  File "/Users/cndws/PycharmProjects/pythonProject/demo.py", line 14, in <module>
    print(next(g))
StopIteration
def generator(n):
    for i in range(n):
        print('开始生成...')
        yield i
        print('完成一次...')
        
g = generator(5)
for i in g:
    print(i)
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 会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行

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

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

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

☆ 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)

☆ 为什么要使用yield生成器

import memory_profiler as mem
​
​
# nums = [1, 2, 3, 4, 5]
# print([i*i for i in nums])
​
​
nums = list(range(10000000))
print('运算前内存:', mem.memory_usage())
# 列表
# square_nums = [n * n for n in nums]
# 生成器
square_nums = (n * n for n in nums)
print('运算后内存:', mem.memory_usage())

☆ yield与斐波那契数列

数学中有个著名的斐波拉契数列(Fibonacci)

要求:数列中第一个数为0,第二个数为1,其后的每一个数都可由前两个数相加得到:

例子:1, 1, 2, 3, 5, 8, 13, 21, 34, ...

现在我们使用生成器来实现这个斐波那契数列,每次取值都通过算法来生成下一个数据, ==生成器每次调用只生成一个数据,可以节省大量的内存。==

def fib(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b      # 使用 yield
        # print b 
        a, b = b, a + b 
        n = n + 1
 
for n in fib(5): 
    print n

3、深浅拷贝

☆ 几个概念

  • 变量:是一个系统表的元素,拥有指向对象的连接空间

  • 对象:被分配的一块内存,存储其所代表的值

  • 引用:是自动形成的从变量到对象的指针

  • 类型:属于对象,而非变量

  • 不可变对象:一旦创建就不可修改的对象,包括数值类型、字符串、布尔类型、元组

(该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。)

  • 可变对象:可以修改的对象,包括列表、字典、集合

(该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。)

当我们写:

a = "python"

Python解释器干的事情:

① 创建变量a

② 创建一个对象(分配一块内存),来存储值 'python'

③ 将变量与对象,通过指针连接起来,从变量到对象的连接称之为引用(变量引用对象)

☆ 赋值

赋值: 只是复制了新对象的引用,不会开辟新的内存空间。

并不会产生一个独立的对象单独存在,只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。

☆ 浅拷贝

浅拷贝: 创建新对象,其内容是原对象的引用。

浅拷贝之所以称为浅拷贝,是它仅仅只拷贝了一层,拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。

注:不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用

浅拷贝有三种形式: 切片操作,工厂函数,copy模块中的copy函数。

如: lst = [1,2,[3,4]]

切片操作:lst1 = lst[:] 或者 lst1 = [each for each in lst]

注:[:]它与[0:]相似,意思是从0索引拆分到末尾。它返回一个新列表。

工厂函数:lst1 = list(lst)

copy函数:lst1 = copy.copy(lst)

但是在lst中有一个嵌套的list[3,4],如果我们修改了它,情况就不一样了。

浅复制要分两种情况进行讨论:

1)当浅复制的值是不可变对象(字符串、元组、数值类型)时和“赋值”的情况一样,对象的id值(id()函数用于获取对象的内存地址)与浅复制原来的值相同。

2)当浅复制的值是可变对象(列表、字典、集合)时会产生一个“不是那么独立的对象”存在。有两种情况:

第一种情况:复制的对象中无复杂子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。

第二种情况:复制的对象中有复杂子对象(例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。 但是改变原来的值中的复杂子对象的值会影响浅复制的值。

☆ 深拷贝

深拷贝:和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。

所以改变原有被复制对象不会对已经复制出来的新对象产生影响。只有一种形式,copy模块中的deepcopy函数。

☆ 案例演示

案例1:对于可变对象深浅拷贝

import copy
a=[1,2,3]
​
print("=====赋值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))
​
print("=====浅拷贝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))
​
print("=====深拷贝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))

结果:

=====赋值=====
[1, 2, 3]
[1, 2, 3]
37235144
37235144
=====浅拷贝=====
[1, 2, 3]
[1, 2, 3]
37235144
37191432
=====深拷贝=====
[1, 2, 3]
[1, 2, 3]
37235144
37210184

小结:

赋值: 值相等,地址相等

copy浅拷贝:值相等,地址不相等

deepcopy深拷贝:值相等,地址不相等

案例2:对于可变对象深浅拷贝(外层改变元素)

import copy
l=[1,2,3,[4, 5]]
​
l1=l #赋值
l2=copy.copy(l) #浅拷贝
l3=copy.deepcopy(l) #深拷贝
l.append(6)
​
print(l)  
print(l1)
print(l2)
print(l3)

结果:

[1, 2, 3, [4, 5], 6]     #l添加一个元素6
[1, 2, 3, [4, 5], 6]     #l1跟着添加一个元素6
[1, 2, 3, [4, 5]]        #l2保持不变
[1, 2, 3, [4, 5]]        #l3保持不变

小结:

① 外层添加元素时,浅拷贝不会随原列表变化而变化;内层添加元素时,浅拷贝才会变化。 ② 无论原列表如何变化,深拷贝都保持不变。 ③ 赋值对象随着原列表一起变化。

本期的笔记分享就到这里啦,下期分享正则表达式!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值