一、局部变量和全局变量
首先来谈一下变量的问题,主要是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(对于复制,深拷贝,浅拷贝讲的非常好,真心推荐!)