Python开发-026_nolocal_深浅拷贝_yield_from

1 nolocal关键字

我们之前已经了解过global关键字,它可以将局部作用域的变量变成全局作用域变量

name = 'root'
def outer():
    name = "abc"
    def inner():
        global name # 修改的是全局变量
        name = 123
    inner()
    print(name) # outer作用域中有name 所以还是abc
outer()
print(name) # 123 全局变量被修改为了123

nolocal关键字修改的是它上一层的局部变量,而且它必须是放在第三层变量,放在第二层不会改变全局变量反而会报错

name = 'root'
def outer():
    name = 'abc'
    def inner():
        nonlocal name
        name = 123
    inner()
    print(name) # 123 nonlocal值修改上一层不修改全局变量
outer()
print(name) # root

2 深浅拷贝

深浅拷贝一般说的都是可变类型:set、list、dict,不可变类型在进行深浅拷贝都是无意义的

image-20220427165032896

我们使用一张图梳理一下,常用的复制拷贝方式

2.1 赋值

v1 = [1,2,3,4,5]
v2 = v1
print(v1,id(v1)) # [1, 2, 3, 4, 5] 140317387400128
print(v2,id(v2)) # [1, 2, 3, 4, 5] 140317387400128

这样的直接赋值会让v1和v2指向同一块内存地址,对于不可变类型无伤大雅,而对于可变类型,就有可能导致修改v2导致v1也发生变化

v2.append('abc')
print(v1) # [1, 2, 3, 4, 5, 'abc']

2.2 浅拷贝

import copy
v1 = [1,2,3,4,['a','b','c']]
v2 = copy.copy(v1)
print(v1,id(v1)) # [1, 2, 3, 4, ['a', 'b', 'c']] 140667964367616
print(v2,id(v2)) # [1, 2, 3, 4, ['a', 'b', 'c']] 140667964367360

拷贝则与简单直接的赋值不同,它会在内存的另一个区域开辟一块空间存放v2的列表,对于列表本身的运算处理不会影响v1

v2.append(5)
print(v1) # [1, 2, 3, 4, ['a', 'b', 'c']]
print(v2) # [1, 2, 3, 4, ['a', 'b', 'c'], 5]

不过浅拷贝也仅限于此,浅拷贝不会将元素的内存也拷贝过来,所以我们得到的其实是列表外壳的拷贝,而内存中的元素内存地址还是指向原来的内存地址,这就会导致,修改v2中嵌套的列表,会导致v1的值发生变化

print(id(v1[4])) # 140383422721152
print(id(v2[4])) # 140383422721152

# 列表中套的列表中的元素
print(id(v1[4][1])) # 140165184063664
print(id(v2[4][1])) # 140165184063664

v2[4].append(1)
print(v1) # [1, 2, 3, 4, ['a', 'b', 'c', 1]]
print(v2) # [1, 2, 3, 4, ['a', 'b', 'c', 1], 5] # 5是之前修改的

2.3 深拷贝

import copy
v1 = [1,2,3,4,['a','b','c']]
v2 = copy.deepcopy(v1)
print(v1,id(v1)) # [1, 2, 3, 4, ['a', 'b', 'c']] 140667964367616
print(v2,id(v2)) # [1, 2, 3, 4, ['a', 'b', 'c']] 140667964367360
print(id(v1[4])) # 140256185297920
print(id(v2[4])) # 140256185296128
v2[4].append(1)
print(v1) # [1, 2, 3, 4, ['a', 'b', 'c']]
print(v2) # [1, 2, 3, 4, ['a', 'b', 'c', 1]]

深拷贝会将可变元素的内存地址同样拷贝一份放在另一个内存空间,无论怎么修改,他们之间都互不影响

import copy
v1 = [1,2,3,4,['a','b','c']]
v2 = copy.deepcopy(v1)

# 拷贝列表和被拷贝列表的内存空间
print(v1,id(v1)) # [1, 2, 3, 4, ['a', 'b', 'c']] 140667964367616
print(v2,id(v2)) # [1, 2, 3, 4, ['a', 'b', 'c']] 140667964367360

# 可变元素内存空间
print(id(v1[4])) # 140256185297920
print(id(v2[4])) # 140256185296128

# 修改可变元素内部的值
v2[4].append(1)
print(v1) # [1, 2, 3, 4, ['a', 'b', 'c']]
print(v2) # [1, 2, 3, 4, ['a', 'b', 'c', 1]]

2.4 深浅拷贝为什么对不可变类型没有意义?

这是python内部对一个优化方案,对于一个相同的不可变类型(例如字符串),他在创建之初,内存地址的值就无法被改变,任何的操作都不会影响它本身,所以python处于性能考虑在定义两个值相同的不可变类型时,就会指向同一个内存空间

v1 = 'abcdefg'
v2 = 'abcdefg'
print(id(v1)) # 140153775805424
print(id(v2)) # 140153775805424

所以深浅拷贝对于不可变类型没有任何意义,无论怎么拷贝,值相同,指向的内存空间就相同

2.5 元组面对拷贝的处理办法

总所周知,元组是个不可变类型,面对不可变类型,无论拷贝还是赋值,相同的值指向同一块内存空间,但元组也是一个特殊的不可变类型,因为它的内部可以嵌套可变类型

import copy
v1 = (1,2,3,[1,2,3])
v2 = v1
v3 = copy.copy(v1)
v4 = copy.deepcopy(v1)

print(v1,id(v1)) # (1, 2, 3, [1, 2, 3]) 140592533849568
print(v2,id(v2)) # (1, 2, 3, [1, 2, 3]) 140592533849568
print(v3,id(v3)) # (1, 2, 3, [1, 2, 3]) 140592533849568
print(v4,id(v4)) # (1, 2, 3, [1, 2, 3]) 140592533849328 # 内存地址发生改变

# 深拷贝直接将内部的可变类型拷贝了一份
print(id(v1[3])) # 140576830204928
print(id(v4[3])) # 140576830202176

image-20220427181408920

3 yield from

def v1():
    yield 1
    yield 1
    yield from v2()
    yield 1
    yield 1

def v2():
    yield 2
    yield 2

for item in v1():
    print(item,end=" ")
# 1 1 2 2 1 1 

yield from其实就是在指向生成器的时候,遇到了from 就跳转到另一个生成器继续执行


Python开发-026_nolocal_深浅拷贝_yield_from
http://localhost:8080/archives/5Tmtb8ZY
作者
kinght
发布于
2024年11月11日
更新于
2024年11月11日
许可协议