python中变量的存储与拷贝

python的所有变量都是引用类型,也就是说每个变量至少包括两个部分:存放变量引用的内存块和存放变量内容的内存块
变量存储
以字符串为例
str
由于字符串是不可变类型,如果改变已经创建字符串的内容,将执行替换操作,将变量指向一块新的内存。

s = 'abcd'
print(s,':',id(s))
s = 'ijmn'
print(s,':',id(s))

abcd : 1390779247728
ijmn : 1390779248120

再来看看可变类型list
在这里插入图片描述
上图展示了append和重新对元素赋值两个操作,都不改变lst的地址指向。但是lst[2]的指向改变(因为替换操作)。

现在可以回头来看函数传参的问题
当我们传递一个参数给函数时候,函数将会对传入参数的地址创建一个副本。
在这里插入图片描述
圆圈2为传参时函数建立的传入变量(圆圈1)的副本,他们拥有共同的指向(圆圈3)。那么显然在函数中,如果将副本重新指向一块新的内存,传入变量(圆圈1)的指向并不会改变,换句话说这种操作并不能影响函数外的变量。

那么什么操作会影响传入变量呢?
  因为副本和传入变量拥有相同的内存指向,如果我们在函数中对这块共同指向的内存(圆圈3)做改变,那么改变就将会影响传入变量,比如对list变量进行append操作。
再来思考传入可变与不可变变量的区别。
  因为改变不可变变量时,通常会使得副本重新指向新的内存块,所以这种操作不会影响传入变量。但是也有例外,比如tuple变量中包含list变量,在函数中对传入的tuple变量中的list元素进行操作,将会影响传入变量的值(这可以从上述方法中得到解释)。

var = (10, [0, 0])
def func(var):
    var[1].append(1)
    print('in function:', var)
func(var)
print('global:',var)

in function: (10, [0, 0, 1])
global: (10, [0, 0, 1])

顺带提一提类中的实例变量与类变量的操作问题

class A:
    num = 10
    lst = [0]
    def f(self):
        self.num = 100
        self.t = 100
    def f2(self):
        self.lst.append(1)
        
a = A()
print('a.num:', a.num, 'A.num:', A.num)
a.f()
print('impact of f()')
print('a.num:', a.num, 'A.num:', A.num)
print('\n')
print('a.lst:', a.lst, 'A.lst:', A.lst)
a.f2()
print('impact of f2()')
print('a.lst:', a.lst, 'A.lst:', A.lst)

原则上讲类变量应该用类名字引用,但是用实例引用(self.var)的方式引用也并不会报错。程序结果如下:

a.num: 10 A.num: 10
impact of f()
a.num: 100 A.num: 10

a.lst: [0] A.lst: [0]
impact of f2()
a.lst: [0, 1] A.lst: [0, 1]

这说明用实例引用类变量的时候,实际上也是创建了一个副本,走的就是函数传参的那一套。

拷贝问题
主要有三个方式:

  • 建立副本 (传参和复制)
a = [1, 2, [0]]
b = a
b[0] = 100
print(a)

b的改变将影响a

  • 浅拷贝(拷贝一层引用)
a = [1, 2, [0]]
b = a.copy()#b = a[:]
b[0] = 'b'
print('a:', a)
print('b:', b)

a: [1, 2, [0]]
b: [‘b’, 2, [0]]

a = [1, 2, [0]]
b = a.copy()#b = a[:]
b[2][0] = 'b'
print('a:', a)
print('b:', b)

a: [1, 2, [‘b’]]
b: [1, 2, [‘b’]]

可以看到,一层拷贝保证了第一层引用的改变不会相互影响,但是第二层改变是将影响被拷贝对象。

  • 深拷贝(将原变量的结构完全复制一遍)
import copy
a = [1, 2, [0]]
b = copy.deepcopy(a)#b = a[:]
b[2][0] = 'b'
print('a:', a)
print('b:', b)

a: [1, 2, [0]]
b: [1, 2, [‘b’]]

深拷贝是将原变量内容结构完全复制一遍到新的空间,两者的内存指向已经完全不沾边了,所以怎么改变都不会相互影响。

ps…作者认知有限,如有错误或者疑问请留言联系我,谢谢阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值