从第八章开始,开始学习 Python 的面向对象特性。
[TOC]
Demo:
a = [1, 2, 3]b = a
Description:
在 CPython 解释器中,id()
返回的是对象的内存地址(unique),在其他 Python 解释器中,不一定。
is 比较的是对象的 id。a == b
本质上是一个语法糖,其会被解释成 a.__eq__(b)
, 不同的对象,其 __eq__()
方法会进行不同的重写,例如字符串会将其重写成对每个字符进行比较,list 会将其重写成对每个元素进行比较。由于 ==
被不同的 built-in types 进行了不同的重写,所以其执行速度往往要慢于 is,is 只是通过 id() 获取内存地址,然后比较,是非常快的。
Demo:
from copy import copyl1 = [1, [2, 3], (4, 5, 6)]# list 浅拷贝的三种方式l2 = list(l1)l3 = l1[:]l4 = copy(l1)l1 == l2 == l3Truel1 is l2Falsel1 is l3Falsel1[1] is l2[1]l2[1] is l3[1]Truel1[1].append(7)l2[1][2, 3, 7] # 引用指向的地址空间相同,变了l1[2] += (8,9)l2[2](4, 5, 6) # 没变,+= on tuple 会创建一个新的 tuple
之所以 l1 is l2 结果为 False,是因为浅拷贝只是拷贝了容器,容器内的元素只是拷贝了其引用,这样可以节省内存。
浅拷贝只是拷贝了一个空壳,其内容都是引用:
from copy import deepcopy
避免将 mutable objects 作为函数参数的默认值
Demo:
class HauntedBus:def __init__(self, passengers=[]): # 设置默认值为 mutable objectself.passengers = passengersdef pick(self, name):self.passengers.append(name)def drop(self, name):self.passengers.remove(name)bus1 = HauntedBus([1,2])print(bus1.passengers) # [1, 2]bus2 = HauntedBus() # 启动默认值bus2.pick(12)print(bus2.passengers) # [12]bus3 = HauntedBus()print(bus3.passengers) # [12],初始化后默认有了参数bus4 = HauntedBus([1,2,3,4])print(bus4.passengers) # [1, 2, 3, 4]bus5 = HauntedBus()bus5.pick(14)print(bus5.passengers) # [12, 14],不断增加
上面的做法,实际上 self.passengers 指向了外部的 list,导致类封装失败。
正确的做法,用浅拷贝将传入的参数进行封装,使得 Bus 拥有了自己的属性,我们对于 passenger 的操作,不会影响到初始化时传入的引用对应的内存地址,我们操作的只是其引用:
class HauntedBus:def __init__(self, passengers=None): # 设置为 Noneif passengers is None:self.passenger = []else:self.passenger = list(passenger) # 用浅拷贝将传入的参数进行封装def pick(self, name):self.passengers.append(name)def drop(self, name):self.passengers.remove(name)
del 命令只会删除引用,并不会删除对象,也就是说,并不会回收地址空间。del 命令本质上也是一个语法糖,其会被 Python 解释器 CPython 解释成魔法方法: __del__()
Python 的垃圾回收算法主要为:reference counting(引用计数),当一个对象对应的引用数为 0 时,也就是说,没有引用指向该内存空间时,则回收该对象的内存空间。每 del 一个 reference,引用数 - 1。
Interning 现象是 Python 解释器的优化导致的,其目的应该是为了节约内存。
驻留现象告诉我们,对于 Python 的字符串和数值型类型的比较,应该用 ==,不能用 is 来比较。
name1 = "wansho"name2 = "wansho"name1 is name2 # true,实际上这应该是两个完全不同的对象
num1 = 1num2 = 1id(num1) == id(num2) # truenum1 = 100num2 = 100id(num1) == id(num2) # truenum1 = 1000num2 = 1000id(num1) == id(num2) # false