Python中的不可变对象和可变对象、Python对于不可变对象列表和可变对象列表做列表乘法

版权声明:站在巨人的肩膀上学习。 https://blog.csdn.net/zgcr654321/article/details/84667747

Python中的不可变对象和可变对象:

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

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

在Python中,数值类型(intfloat)、字符串str、元组tuple都是不可变类型。而列表list、字典dict、集合set是可变类型。

对于不可变对象

不可变对象的值改变前后的内存地址变化:

is 是判断两个对象的id是否相同, 而 == 判断的是内容是否相同。

如果是int或float型数值:

a = 2
b = a
c = 2

print(id(a), id(b), id(c))
print(a is b)
print(a == b)
print(c is b)
c = a + 1
print(id(a), id(b), id(c))
print(c is b)

运行结果如下:

503147568 503147568 503147568
True
True
True
503147568 503147568 503147600
False

Process finished with exit code 0

如果是字符串:

a_str = 'good'
b_str = 'good'
c_str = a_str + ''
print(a_str is b_str)
print(c_str is b_str)
print(id(a_str), id(b_str), id(c_str))
a_str = a_str + 'aa'
print(id(a_str), id(b_str), id(c_str))

运行结果如下:

True
True
37597624 37597624 37597624
42819064 37597624 37597624

Process finished with exit code 0

不可变对象变量对应内存的值不允许被改变。因此不可变对象的变量值要改变时,实际上是把原来的值复制一份后再改变,开辟一个新的内存地址,astr再指向这个新的内存地址,所以改变前后的id不一样,原来astr对应的值因为不再有对象指向它,就会被垃圾回收。

特殊情况:元组(tuple)

add = (1, 2, 3)
aee = (1, 2, 3)
print(id(add), id(aee), id((1, 2, 3)))
# 加空元组
aee += ()
print(add, aee)
print(id(add), id(aee), id((1, 2, 3)))
aff = add
print(id(add), id(aff))
aff += (4, 5, 6)
print(id(add), id(aff))
print(add, aff)

运行结果如下:

42887640 42976744 42921416
(1, 2, 3) (1, 2, 3)
42887640 43022232 43021656
42887640 42887640
42887640 42979592
(1, 2, 3) (1, 2, 3, 4, 5, 6)

Process finished with exit code 0

add和aee都是(1,2,3),但是id却不一样。因为我们是单独给add和aee都定义为(1,2,3)。当我们给aee加上一个空元组后,aee的id又变化了。aff=add,所以aff和add的id和内容就都是一样的。当我们修改了aff时,aff的id变化了。

tuple是不可变对象,但又和str和数值类型稍微有点区别。平常说的tuple不可变更多时候是指里面存放的值不能被改变(有些特殊情况,如tuple里面存放了list,可改变list里的元素。但实际上这个tuple并没有被改变)。

对于str、int、float,只要变量的类型和值都相同,那么变量的id也相同。

可变对象的值改变前后的内存地址变化:

如果是列表:

l1 = [1, 2, 3]
l2 = [1, 2, 3]
print(l1 is l2)
print(id(l1), id(l2), id([1, 2, 3]))
l3 = l2
print(id(l1), id(l2), id(l3))
l3.append(4)
print(l2, l3)
print(id(l1), id(l2), id(l3))

运行结果如下:

False
40303368 40303496 40941704
40303368 40303496 40303496
[1, 2, 3, 4] [1, 2, 3, 4]
40303368 40303496 40303496

Process finished with exit code 0

l3=l2,那么l3世纪上是对l2的引用,我们可以看到l3和l2的id一样。l1和l2的内容一样,但id却不一样。

当我们改变l3时,由于l3是对l2的引用,故l2的内容也改变了。

如果是set:

abb = {1, 2, 3}
acc = abb
print(id(abb), id(acc))
acc.add(4)
print(abb, acc)
print(id(abb), id(acc))

运行结果如下:

43093128 43093128
{1, 2, 3, 4} {1, 2, 3, 4}
43093128 43093128

Process finished with exit code 0

acc是对abb的引用,所以改变acc的内容,abb的内容也改变了。

注意:

如果是拷贝函数,就仅仅是将内容拷贝过去,传递的并不是引用。这在想使用列表的值又不想修改原列表的时候特别有用。

如:

l1 = [1, 2, 3]
l2 = l1.copy()
print(l1, l2)
print(id(l1), id(l2))
l2.append(4)
print(l1, l2)
print(id(l1), id(l2))

运行结果如下:

[1, 2, 3] [1, 2, 3]
42072840 42072968
[1, 2, 3] [1, 2, 3, 4]
42072840 42072968

Process finished with exit code 0

此时修改l2,l1的内容不变。

作为函数参数时,可变类型传递的是引用,不可变类型传递的是内容:

如:

l1 = [1, 2, 3, 4]
str1 = 'HELLO'


def listchange(list_a):
	list_a.append(5)
	return list_a


def strchange(astr):
	astr = astr.lower()
	return astr


l_c = listchange(l1)
str_c = strchange(str1)
print(l1, l_c)
print(id(l1), id(l_c))
print(str1, str_c)
print(id(str1), id(str_c))

运行结果如下:

[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
40172296 40172296
HELLO hello
37728696 42884040

Process finished with exit code 0

list是可变类型,传递进函数的是引用,因此修改list_a内容后l1的内容也变化了;str是不可变类型,传递仅函数的是内容,修改astr后str1的内容不变。

Python对于不可变对象列表和可变对象列表做列表乘法:

如:

a = [1]
b = a * 4
print(a, b)
print(id(a), id(b[0]), id(b[1]), id(b[2]), id(b[3]))
a[0] = 2
print(a, b)
print(id(a), id(b[0]), id(b[1]), id(b[2]), id(b[3]))

c = [{'key': 1}]
d = c * 4
print(c, d)
print(id(c), id(d[0]), id(d[1]), id(d[2]), id(d[3]))
d[0]['key'] = 2
print(c, d)
print(id(c), id(d[0]), id(d[1]), id(d[2]), id(d[3]))

运行结果如下:

[1] [1, 1, 1, 1]
42138376 500329488 500329488 500329488 500329488
[2] [1, 1, 1, 1]
42138376 500329488 500329488 500329488 500329488
[{'key': 1}] [{'key': 1}, {'key': 1}, {'key': 1}, {'key': 1}]
42776712 38170200 38170200 38170200 38170200
[{'key': 2}] [{'key': 2}, {'key': 2}, {'key': 2}, {'key': 2}]
42776712 38170200 38170200 38170200 38170200

Process finished with exit code 0

a中的元素1是int型,是不可变对象,做列表乘法后,b中的四个1都是同一个新地址,修改a[0]后,b不受影响。

c中的元素是一个字典,是可变对象,做列表乘法后,d中的4个元素都是同一个新地址,修改c[0]后,d中的4个元素的值也一起改变了。这说明d中的4个元素都是c中字典对象元素的引用。

猜你喜欢

转载自blog.csdn.net/zgcr654321/article/details/84667747