天天看点

对象引用、可变性和垃圾回收python的变量到底是什么==和is的区别del语句和垃圾回收一个经典的小错误

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

  • python的变量到底是什么
  • ==和is的区别
  • del语句和垃圾回收
  • 一个经典的小错误

python的变量到底是什么

==和is的区别

在讲is和==这两种运算符区别之前,首先要知道Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。

==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等,例如下面两个字符串间的比较:

>>> a = 'cheesezh'
>>> b = 'cheesezh'
>>> a == b
True
           

is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。通过对下面几个list间的比较,你就会明白is同一性运算符的工作原理:

>>> x = y = [4,5,6]
>>> z = [4,5,6]
>>> x == y
True
>>> x == z
True
>>> x is y
True
>>> x is z
False
>>>
>>> print id(x)
>>> print id(y)
>>> print id(z)
           

下面再来看一个例子,例3中同一类型下的a和b的(a==b)都是为True,而(a is b)则不然。

>>> a = 1 #a和b为数值类型
>>> b = 1
>>> a is b
True
>>> id(a)
>>> id(b)
>>> a = 'cheesezh' #a和b为字符串类型
>>> b = 'cheesezh'
>>> a is b
True
>>> id(a)
>>> id(b)
>>> a = (1,2,3) #a和b为元组类型
>>> b = (1,2,3)
>>> a is b
False
>>> id(a)
>>> id(b)
>>> a = [1,2,3] #a和b为list类型
>>> b = [1,2,3]
>>> a is b
False
>>> id(a)
>>> id(b)
>>> a = {'cheese':1,'zh':2} #a和b为dict类型
>>> b = {'cheese':1,'zh':2}
>>> a is b
False
>>> id(a)
>>> id(b)
>>> a = set([1,2,3])#a和b为set类型
>>> b = set([1,2,3])
>>> a is b
False
>>> id(a)
>>> id(b)
           

通过例3可看出,只有数值型和字符串型的情况下,a is b才为True,当a和b是tuple,list,dict或set型时,a is b为False。

del语句和垃圾回收

Python中的垃圾回收算法是采用引用计数, 当一个对象的引用计数为0时, Python的垃圾回收机制就会将对象回收

a = "larry"
b = a
# larry这个字符串对象, 在第一行被贴了a标签后, 引用计数为1, 之后在第二行, 由贴上了b标签
# 此时, 该字符串对象的引用计数为2
           
a = "larry"
b = a
del a
# 注意: 在Python语言中, del语句操作某个对象的时候, 并不是直接将该对象在内存中删除,
# 而是将该对象的引用计数-1
           

魔法函数之__del__

类中的__del__魔法函数, 支持我们自定义清理对象的逻辑, 当Python解释器使用del语言删除类的对象的时候, 会自动调用类中的__del__函数, 我们可以对其进行重载

>>> class Ref:
...
...   def __init__(self, name):
...     self.name = name
...
...   def __del__(self):
...     print("删除对象")
...     del self.name
...
>>>
>>> r = Ref(name="larry")
>>> print(r.name)
larry
>>>
>>> del r
           

我们可以通过重载__del__魔法函数, 自己灵活控制在del 对象的时候执行哪些善后操作

一个经典的小错误

class Company:
    def __init__(self, myTitle, myLists=[]):
        self.myTitle = myTitle
        self.myLists = myLists
    def add_List(self, myList):
        self.myLists.append(myList)
    def remove_List(self, myList):
        self.myLists.remove(myList)

if __name__ == "__main__":
    com1 = Company("bai", ['bai1', 'bai2'])
    com1.add_List('bai3')
    com1.remove_List('bai1')
    print(com1.myLists) # ['bai2', 'bai3']

    com2 = Company("lai")
    com2.add_List('lai1')
    print(com2.myLists) # ['lai1']

    com3 = Company("lai")
    com3.add_List('lai2')

    # 这里com2.myLists并不是输出的['lai1']
    # 1、第二个参数是一个列表,是一个可变的对象
    # 2、com2和com3 都没有传递myLists参数,都会使用默认的空
    # 所以说com2和com3的myLists都是共用的同一个对象
    print(com2.myLists) # ['lai1', 'lai2']
    print(com3.myLists) # ['lai1', 'lai2']
    # 说明com3.myLists 和 com2.myLists 是同一个对象
    print(com3.myLists is com2.myLists)  # True
    print(Company.__init__.__defaults__) # (['lai1', 'lai2'],)

    # 结论:尽量不要传递可变的对象,如必须要传递,务必了解以上情况