python基础之局部变量、全局变量、变量的复制、深拷贝、浅拷贝

一、局部变量和全局变量

首先来谈一下变量的问题,主要是Python内在的变量处理机制,这里主要探讨一下局部变量和全局变量的问题:

我们先看一下下面的代码会输出什么:

# 代码段 1
a = 7
b = 5
def f1(a):
    a += 1
    print(a)
    print(b)

f1(a)

# 代码段 2
def f2(a):
    print(a)
    print(b)
    b = 7

a = 4
b = 8
f2(a)

第一题是不是比较简单,输出的结果就是:8, 5

而第二题的结果会报错,从错误信息 local variable 'b' referenced before assignment 中,我们可以知道变量b在使用前没有定义没有定义。这是为什么呢?在调用f2之前我们不是定义了全局变量b=8嘛?

在函数内部,如果一个变量名出线在赋值语句的左边(即 “=” 号的左边),那么我们就将此变量作为函数内部的局部变量,而不管该变量具体出现在函数的前半部分还是后半部分。

代码段 1 中,我们可以知道,变量a是作为局部变量存在的,而变量b由于在函数中并没有出现在赋值语句的左边,所以b是作为全局变量存在的。而代码段 2 中的b显然是一个局部变量,虽然print操作在b赋值操作之前。(前面讲了,只要在函数体内变量出现在了“=”的左边,那么该变量就是局部变量,而不存在前一半部分是全局变量,后一半部分又作为局部变量的情况。局部变量的优先级大于全局变量,当一个变量同时存在同名的局部变量和全局变量的时候,优先使用局部变量

对于局部变量,在使用前,我们需要先赋值,后使用。在代码段2中,我们发现b作为一个局部变量,但是在没有定义之前就进行引用了,所以会报错。

代码段2,我们想先输出全局变量的值,然后对变量重新赋值的话,有没有什么处理方法呢。也是可以解决的,这就需要使用global 关键字。global 将变量b作为全局变量,就可以在定义之前对全局变量进行引用了。同时函数内部对全局变量进行了重新赋值,所以最后b的值变为了7.

# 代码段 2 的修改
def f2(a):
    global b
    print(a)
    print(b)
    b = 7

a = 4
b = 8
f2(a)
print(b)

# 代码输出结果:4,8,7

以上代码我们使用的变量都是不变量,当输入的数据是list等可变对象的时候,结果又是怎么呢?

我们再来看一下下面的代码:

# 代码段 3
def func(a):
    a.append(3)
    print(a)

a = [1,2]
print(a)
func(a)
print(a)

# 输出:[1,2], [1,2,3], [1,2,3]

# 代码段 4
def func(a):
    a = [4,5]
    a.append(3)
    print(a)

a = [1,2]
print(a)
func(a)
print(a)

# 输出为:[1, 2], [4,5,3], [1, 2]

当参数是一个可变对象并且作为全局变量出现的时候,对其进行修改,也会影响到全局变量的值,同理对于局部变量的修改并不会影响全局变量的值。

二、复制,深拷贝,浅拷贝

首先还是先来看一下,这几个代码,看看会输出什么。

# 代码段 4
a = [1,2,3, [4,5], (7, 8)]
b = a
a.append(10)
print(a)
print(b)

# out: [1, 2, 3, [4, 5], (7, 8), 10],[1, 2, 3, [4, 5], (7, 8), 10]

# 代码段 5
a = [1,2, [3,4,5], (6, 7)]
b = list(a)
def f5(a):
    a.append(10)
    a[2].remove(5)
    print(a)
    print(b)
f5(a)

# out:[1, 2, [3, 4], (6, 7), 10], [1, 2, [3, 4], (6, 7)] 

# 代码段 6
import copy
a = [1,2, [3,4,5], (6, 7)]
b = copy.deepcopy(a)
def f6(a):
    a += [0, 10]
    a[2].remove(5)
    print(a)
    print(b)
f6(a)

# out:[1, 2, [3, 4], (6, 7), 0, 10], [1, 2, [3, 4, 5], (6, 7)]

代码 4 中b是a的一个复制,复制过程中b指向a的地址,对a做的任何修改都将反映到b上。可以这样说,a、b是同一个值。

代码 5 中b是a的一个浅拷贝(list和[:]切片操作都是浅拷贝),浅拷贝过程中,b和a的地址是不一样的,但是b中的每一个元素和a中的每一个元素都是指向相同的地址。即内部地址是一样的。修改a中不可变对象时,并不会影响到b中对应位置的元素。这是因为替换的新的元素会分配一个新的地址,而b并不指向新的地址。当修改a中的可变对象时,比如代码 5 中a[2].remove(5), 由于修改可变对象,并没有修改其地址。所以,对a中可变对象的修改会影响到b中对应位置的对象。

代码 6 中通过copy.deepcopy 方法实现深拷贝的操作,深拷贝过程中不仅a和b两个变量拥有不同的地址,内部的元素也拥有不同的元素,所以,在对其中一个变量进行修改的时候,完全不会影响到另一个元素。

# 参考资料:

https://www.cnblogs.com/wilber2013/p/4645353.html(对于复制,深拷贝,浅拷贝讲的非常好,真心推荐!)

猜你喜欢

转载自blog.csdn.net/weixin_39561100/article/details/82803511