开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情
对象引用,可变性和垃圾和回收
一,python中的变量是什么
python 和 java 变量本质不一样,java 每次创建变量就要声明类型就像一个盒子,指定了里面要装什么,就只能装什么,python 的变量本质就是一个指针,里面存的是数据的地址,指针里面的内存地址可以被修改,指向不同的对象,这个是动态语言实现的核心
>>> a = [1, 2, 3]
>>> b = a
>>> b.append(4)
>>> a
[1, 2, 3, 4]
>>> a is b
True
>>> print(id(a), id(b))
521210, 521210
二,is 和 == 的区别
is :在内存中的地址知否相同,是否是同一个对象
== :判断值是否相同
python 内部有一个小整数,小段字符串,在内存空间里是指向同一个对象
>>> a = '122'
>>> b = '122'
>>> id(a), id(b)
True
三,del语句和垃圾回收
python 中垃圾回收的算法是采用 引用计数模式
什么意思呢?
就是如果 a = 'abc' ,底层就会有一个计数器计算值 'abc' 被几个变量指向,如果指向数量为0,则自动回收
>>> a = 'abc' # 创建一个对象指针a,在内存中指向值'abc'的内存地址,底层计数器+1
>>> b = a # 创建一个指针b,在内存中指向'abc',底层计数器+1
>>> del a # 删除a的指向,底层计数器-1
>>> b # 'abc'的值还存在
'abc'
>>> del b # 删除a的指向,底层计数器-1,现在'abc'的指向为0,python将他自动回收
class A:
def __del__(self): # 当实例对象被删除会自动执行
pass
四,一个经典的参数错误
不要使用可变类型作为参数的默认值
下面用一辆诡异的幽灵车说明问题
class HauntedBus:
"""备受幽灵乘客折磨的校车"""
def __init__(self, passengers=[]):
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
>>> h1 = HauntedBus() ①
>>> h2 = HauntedBus() ②
>>> h1.pick('我上h1的车') ③
>>> h2.passengers ④
['我上h1的车']
>>> print(id(h1.passengers), id(h2.passengers)) ⑤
2622174849224 2622174849224
>>> h3 = HauntedBus()
>>> h3.__init__.__defaults__
(['我上h1的车'],) ⑥
① 实例化第一辆车
① 实例化第二辆车
③ 一个乘客上了第一辆车
④ 奇异的一幕发生了,这位乘客既然在第二辆车上,这就奇怪了,打印下id地址看下
⑤ 我们发现,两辆车的id地址居然一样!!!
⑥ 当我们再创建一辆 空 车的时候,发现他默认会绑定前面的类留下的值
还有更加诡异的车
class TwilightBus:
"""让乘客销声匿迹的校车"""
def __init__(self, passengers=None): ①
if passengers is None:
self.passengers = []
else:
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
if __name__ == '__main__':
people = ['A', 'B', 'C', 'D', 'E', 'F'] ②
c = TwilightBus(people)
print('我是c里面的passengers的值', c.passengers)
c.drop('A') ③
print('我是people的值', people)
...
我是c里面的passengers的值 ['A', 'B', 'C', 'D', 'E', 'F']
我是people的值 ['B', 'C', 'D', 'E', 'F']
① 我们在这里将默认值的类型从可变类型变成 None,是一个极大的改进,预防不可估计的错误
② 创建一个列表,里面有这么多同学
③ 把实例的乘客删除一个
④ 更加诡异的事情发生了,当我们修改实例的属性,我们发现列表的属性被修改了
变量指向同一个内存地址 !!!!!!!
这里是我们需要理解的关键,我们之前说过,变量是一个指针,他的作用是指向一块内存
self.passengers = passengers 这句话把 self.passengers 这个变量指向了passengers的内存地址,也就是传入的列表
- 所以,在我们修改 self.passengers 的值,外面的列表会发生改变
函数的参数的最好的定义方法
class TwilightBus:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers) # 这里只是浅复制
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)