python深入学习(6):对象引用,可变性和垃圾和回收

123 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情

对象引用,可变性和垃圾和回收

一,python中的变量是什么

pythonjava 变量本质不一样,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

四,一个经典的参数错误

[外链图片转存中...(img-rnwJXavH-)] 不要使用可变类型作为参数的默认值

下面用一辆诡异的幽灵车说明问题

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)