文章目录

  • 1.对象和对象的引用
  • 2.可变对象和不可变对象
  • 3.两种对象在内存中的存储
  • 3.两种对象更改数值时的内存
  • 4.两种对象的赋值和复制
  • 不可变对象
  • 可变对象
  • 5.两种对象的+=和=+
  • 6.两种对象的函数传参



今天在看一个递归的时候列表赋值又把我绕晕了,这个点每次看每次忘,所以来总结一下

1.对象和对象的引用
  • 对象:指在内存中存储的数据,每个对象包含一个头部信息,有类型表示符和引用计数器
  • 对象的引用:是一个变量,通过指针指向引用对象的内存空间,取对象的值。变量本身没有类型,指向那种类型的对象,就体现为哪种类型。

以下a变量就是对象的引用,1是a指向的内存中存储的数据

a = 1

python类无法调用属性 python对象不可调用_python类无法调用属性

2.可变对象和不可变对象
  • 不可变对象类型
  • 数值类型 int,float,long等
  • 字符串 str
  • 元组 tuple
  • 可变对象类型
  • 列表 list
  • 字典 dict
  • 可变 set

可变和不可变的区别:能否在不改变内存id的情况下,改变value值。

3.两种对象在内存中的存储
  • 不可变对象
a = 1

python类无法调用属性 python对象不可调用_python类无法调用属性

  • 可变对象list
b=[1,2,3]

变量b指向容器对象的引用地址

python类无法调用属性 python对象不可调用_不可变对象_03

3.两种对象更改数值时的内存
  • 不可变对象更改数值时的内存
    一个变量指向了不可变对象,如果要更改数值,会在内存中创建一个新对象,把变量指向新对象,或指向已存在对象(此对象被变量引用,计数器+1);旧对象如果没有被引用,就等待垃圾回收。
a=1
print(id(a))  # 140725527819088
a=2
print(id(a)) # 140725527819120

python类无法调用属性 python对象不可调用_不可变对象_04

  • 可变对象更改数值时的内存
    一个变量指向list容器对象, 添加元素时容器对象内存地址id不变,区域的长短会变。
b = [1,2,3]
print(id(b))  # 2155591512904
b.append(4)
print(id(b)) # 2155591512904


python类无法调用属性 python对象不可调用_赋值_05

如果添加的元素是另一个列表,那么此容器对象中新添加的,其实是另一个列表的容器对象的引用

b = [1,2,3]
print(id(b))  # 2155591512904
b.append([4,5])
print(id(b)) # 2155591512904


python类无法调用属性 python对象不可调用_赋值_06

4.两种对象的赋值和复制
不可变对象
a=1
b=a
print(id(a))  # 140725527819088
print(id(b))  # 140725527819088

对于不可变对象来说,变量的赋值时赋的是变量的引用。

python类无法调用属性 python对象不可调用_不可变对象_07

b=2
print(id(a))  # 140725527819088
print(id(b))  # 140725527819120

由于对象不可变,当变量b改变时,会创建新的对象,指向新的内存,对a变量没有影响。

python类无法调用属性 python对象不可调用_不可变对象_08

可变对象
  • 可变对象赋值
a = [1,2,3]
b = a
print(id(a))  #2155591539400
print(id(b))  #2155591539400

对于可变对象来说,赋值也是赋的对象的引用,但是是容器对象的引用。

python类无法调用属性 python对象不可调用_不可变对象_09

b[2] = 4
print(a[2])  # 4

当b的值发生变化时,是容器对象中的某个元素指向的对象发生改变,因此会影响到a.

python类无法调用属性 python对象不可调用_不可变对象_10

  • 可变对象浅拷贝
b=a[:]

b=a.copy()

a=[1,2,3,[4,5]]
b=a[:]
print(id(a))  # 2155591539272
print(id(b))  # 2155591540424

当浅拷贝时,会创建新的容器对象,但这些容器对象指向相同的元素对象。


python类无法调用属性 python对象不可调用_赋值_11

a[0] = 7
print(b[0])  # 1
a[3][1]=6
print(b[3][1]) # 6

当a想要指向另一个不可变对象,对b没有影响;当指向的可变对象某元素改变时,会影响b。


python类无法调用属性 python对象不可调用_赋值_12

  • 可变对象深拷贝
import copy
a=[1,2,3,[4,5]]
b=copy.deepcopy(a)
print(id(a)) #2155591633288
print(id(b)) #2155591513096
a[0] = 7
print(b[0])  # 1
a[3][1]=6
print(b[3][1]) # 5

彻底复制,a,b再也不会引用相同的容器对象。分别指向的容器对象数值更改不会再互相影响。


python类无法调用属性 python对象不可调用_赋值_13

5.两种对象的+=和=+
  • 不可变对象
a=1
print(id(a))  #140725527819088
a += 1
print(id(a)) # 140725527819120
a = a+1
print(id(a)) #140725527819152

三次地址都不同,因为对于不可变对象 += 和=+ 调用的都是 __add__函数;要改变值,变量一定会引用其他的对象。

python类无法调用属性 python对象不可调用_赋值_14

  • 可变对象
b=[1,2,3]
print(id(b))  # 2155591281416
b += [4]
print(id(b))  # 2155591281416
b = b + [4]
print(id(b)) # 2155589911432

对于可变对象,+=时相当于调用__iadd__函数,相当于原地修改,不改变引用对象地址id。

python类无法调用属性 python对象不可调用_python类无法调用属性_15


而调用= + 时调用__add__函数,相当于浅复制.

python类无法调用属性 python对象不可调用_python类无法调用属性_16

6.两种对象的函数传参

函数中实参赋给形参值时,都是传递的对象的引用。但由于两种对象的可变和不可变性质,看起来效果不同。

  • 不可变对象
def func(x):
	x += 1
	return x
a=3
func(a)
print(a) # 3

当a传入函数后,a 的对象(3) 创建了新的引用x,当变量x发生改变时,指向了新的对象;实参a并没有受到影响。

  • 可变对象
def  f1(x):
	x += [1]
a=[1,2]
f1(a)
print(a) # [1,2,1]

a传入函数的是容器对象的引用,所以如果对变量x做类似append和+=的操作时,会改变实参a。

a=[1,2]
f1(a[:])
print(a) # [1,2]

a传入参数的是容器对象的拷贝,不论在函数中对变量x做什么操作,都不会影响实参a。但如果a的元素中有列表,浅拷贝时有影响,深拷贝没有。

def f2(x):
	x = x + [1]
a = [1,2]
f1(a)
print(a) # [1,2]
f1(a[:])
print(a) # [1,2]

由于函数f2中对变量x做了 =+的操作,相当于做了浅拷贝,所以对a没有影响