第七章 对象应用、可变性和垃圾回收
7.1 python中的变量是什么
python和java中的变量本质不一样,java里的变量看做一个盒子(有大有小)。java中声明一个变量,首先要声明变量类型是什么,比如int、str或者某个类。声明这个类之后,虚拟机就会在内存中给我们申请一块空间,空间大小跟类型有关。这个盒子能装什么东西,在开始时就已经指定了。
但是python变量实质是一个指针,这个指针可以指向int类型的数据,也可以指向str类型的数据。也可以理解为便利贴,大小固定,可以贴在任何地方。
a = 1
a = "abc"
#1. a贴在1上面
#2. 先生成对象 然后贴便利贴
过程是
- 首先去内存里申请一个空间放1这个int对象
- 然后让a指向1这个对象(贴在1上面)
为进一步说明便利贴的特性,我们声明一个数组a,然后让b=a,对b修改看结果
a = [1,2,3]
b = a
b.append(4)
print(a)
print(a is b)
print(id(a),id(b))
[1, 2, 3, 4]
True
2527462106944 2527462106944
可以看到,修改b的同时(修改了b对应的对象),a也变化了。a,b贴在同一个对象上面。
7.2 ==和is的区别
is是判断两个对象的id()是否一致,用一个例子来看==是什么
a = [1,2,3,4]
b = [1,2,3,4]
print(a is b)
print(a == b)
False
True
2192327583744 2192327582784
a和b对象不一致(id 不同),所以is判断就是False,但两个对象的值是一样的,==判断就是True。
但是有些特殊情况,需要注意下
a = 1
b = 1
print(a is b)
print(id(a),id(b))
True
140724036245152 140724036245152
可以看到这两个对象是一致的,在python内部有一个intern机制,会把一定范围内的小整数建立一个全局唯一对象(常量池),如上面a=1,当执行b=1时,会指向同一个对象。这个对小串的字符串来说,也是一样的。
a = "abc"
b = "abc"
print(a is b)
print(id(a),id(b))
True
2815963014896 2815963014896
使用==可以看到是True
a = [1,2,3,4]
b = [1,2,3,4]
print(a==b)
print(a is b)
True
False
这个其实和魔法函数有关,list里面有一个魔法函数__eq__,当使用==时,会调用list里的__eq__魔法函数,从而判断值是否相等。
所以判断一个实例是否属于一个类时,要用is进行判断(但是用isinstance更好)
class People:
pass
person = People()
if type(person) is People: # 类也是一个对象,People也是全局唯一的
print ("yes")
yes
7.3 del语句和垃圾回收
1.python中垃圾回收的算法是采用引用计数。
a = 1 # 1这个对象就会有一个计数器,a=1时会在引用计数器上+1
b = a # b指向同一个对象时,计数器再+1
del a # 会将引用计数器-1,当计数器=0时,会将1对象回收,防止一直占用内存
python的del和c++中不同,c++是直接回收对象,而python del直到计数器=0时,才会对对象进行回收。
a = object()
b = a # b,a指向同一个对象
del a # del a,计数器-1,但>0,未被回收
print(b) # 可以打印b
print(a) # a无法打印
<object object at 0x000001CE31B34E10>
Traceback (most recent call last):
File "E:/pyproject/AdvancePython-master/chapter06/test.py", line 5, in <module>
print(a)
NameError: name 'a' is not defined
在cpython2.0中就不是计数器。
2.自己定义一个对象时,可以在__del__中写自己的垃圾回收逻辑
class A:
def __del__(self):
pass
7.4 一个经典的参数错误
第一种情况:
def add(a, b):
a += b
return a
if __name__ == "__main__":
a = 1
b = 2
c = add(a,b)
print(c)
print(a,b)
3
1 2
来看第二种情况:
def add(a, b):
a += b
return a
if __name__ == "__main__":
a = 1
b = 2
a = [1,2]
b = [3,4]
c = add(a,b)
print(c)
print(a,b)
[1, 2, 3, 4]
[1, 2, 3, 4] [3, 4]
c是[1, 2, 3, 4],a也变成[1, 2, 3, 4]了。
再看第三种情况:
def add(a, b):
a += b
return a
if __name__ == "__main__":
a = 1
b = 2
a = (1, 2)
b = (3, 4)
c = add(a, b)
print(c)
print(a, b)
(1, 2, 3, 4)
(1, 2) (3, 4)
c是(1, 2, 3, 4),a还是(1, 2)。
在第1、3种情况下,a没有受到影响。a,b为list的时候,a受到了影响。
再add函数中,a,b是list这种可变类型时,由于+=是就地修改,所以a+=b修改了a。
举另一个例子加深理解:
class Company:
def __init__(self, name, staffs=[]):
self.name = name
self.staffs = staffs
def add(self, staff_name):
self.staffs.append(staff_name)
def remove(self, staff_name):
self.staffs.remove(staff_name)
if __name__ == "__main__":
com1 = Company("com1", ["bobby1", "bobby2"])
com1.add("bobby3")
com1.remove("bobby1")
print(com1.staffs)
['bobby2', 'bobby3']
这里看起来没有什么问题,我们再加两个操作。
class Company:
def __init__(self, name, staffs=[]):
self.name = name
self.staffs = staffs
def add(self, staff_name):
self.staffs.append(staff_name)
def remove(self, staff_name):
self.staffs.remove(staff_name)
if __name__ == "__main__":
com1 = Company("com1", ["bobby1", "bobby2"])
com1.add("bobby3")
com1.remove("bobby1")
print(com1.staffs)
com2 = Company("com2") # 不传list
com2.add("bobby")
com3 = Company("com3") # 不传list
com3.add("bobby5")
print (com2.staffs)
print (com3.staffs)
print (com2.staffs is com3.staffs)
print(Company.__init__.__defaults__)
['bobby2', 'bobby3']
['bobby', 'bobby5']
['bobby', 'bobby5']
True
(['bobby', 'bobby5'],)
com2里面出现了'bobby5',com3里出现了'bobby',com2和com3的值是一样的,这就有问题了。
造成这种情况的原因是:
1.__init__传递的一个列表是可变对象
2.com2和com3都没有传递list进去,所以使用的是同一个默认的list
这个默认list可以获取到,但是com1传入了一个list,就不会指向默认list。这里就有一个结论,尽量不要使用可变类型作为参数。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)