第七章 对象应用、可变性和垃圾回收

7.1 python中的变量是什么

python和java中的变量本质不一样,java里的变量看做一个盒子(有大有小)。java中声明一个变量,首先要声明变量类型是什么,比如int、str或者某个类。声明这个类之后,虚拟机就会在内存中给我们申请一块空间,空间大小跟类型有关。这个盒子能装什么东西,在开始时就已经指定了。

但是python变量实质是一个指针,这个指针可以指向int类型的数据,也可以指向str类型的数据。也可以理解为便利贴,大小固定,可以贴在任何地方。

a = 1
a = "abc"
#1. a贴在1上面
#2. 先生成对象 然后贴便利贴

过程是

  1. 首先去内存里申请一个空间放1这个int对象
  2. 然后让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。这里就有一个结论,尽量不要使用可变类型作为参数。

posted @ 2020-09-10 20:36  yueqiudian  阅读(107)  评论(0编辑  收藏  举报