Introduce
python 的变量是什么
is 和 == 区别
del 语句和垃圾回收
一个经典的参数错误
变量:用来标识(identify)一块内存区域。为了方便表示内存,我们操作变量实质上是在操作变量指向的那块内存单元。编译器负责分配。我们可以使用Python内建函数id()来获取变量的地址
变量名:是一个标识符(dientify),用来代之一块内存空间,使用这个变量名,我们可以很方便的操作这块内存区域。
内存: 内存是我们电脑硬件,用来存放数据,形象的理解就是内存有一个一个的小格子组成,每个格子的大小是一个字节,每个格子可以存放一个字节大小的数据。我们如何才能知道,数据存放在哪些格子中,那就得靠地址,地址类似于楼房的门牌号,是内存的标识符。
id(object)函数是返回对象object在其生命周期内位于内存中的地址,id函数的参数类型是一个对象。
概述
- java 中变量相当于申请一个盒子,盒子有类型大小之说
- python 中变量,类似一个指针,指针的值是固定的,类似便利贴,可以贴到任何对象上
我们通过解析下面一段代码来了解Python的变量是什么
示例代码:
# a 贴到 1 上面 a = 1 # a 再贴到 'abc' 上 a = 'abc' # 注意顺序: 先生成对象,然后贴便利贴 la = [1, 2, 3] lb = la # is, 本质 id(a) 与 id(b) 比较 print(lb is la) # True, id 相同 print(id(la), id(lb)) la.append(100) print(lb) # lb 和 la 指向相同对象,lb 会发生变化
代码解析:
a = 1
过程 :
便利贴的大小是固定的, 所以a可以帖在任何对象上
a = 'abc'
两个指针指向同一个对象(两个便利贴贴在同一个物体上)
a = [1, 2, 3] b = a b.append(4) print(a) # [1, 2, 3, 4] print(a is b) # True
可以看到,修改b的同时(修改了b对应的对象),a也变化了。a,b贴在同一个对象上面。
python赋值方式
“把 变量 分配给 对象”,而不是“把 对象 分配给变量 ”。对 <引用式变量> 来说,说把变量分配给对象更合理,反过来说就有问题。毕竟,对象在赋值之前就创建了。
为了理解 Python 中的赋值语句,应该始终先读右边。对象在 右边创建或获取,在此之后左边的变量才会绑定到对象上,这就像 为对象贴上标注.
is : 比较 id()是否相同
(占用的内存地址是否相同)
== : 比较 变量值 是否相同
(内存地址可以不一样,内容一样就可以了,默认会调用对象的 eq()方法)
# is, 本质 id(a) 与 id(b) 比较 # = 右边为对象时,表示生成新对象 a = [1, 2, 3, 4] b = [1, 2, 3, 4] print(a is b) # False, 说明 id 不同 print(id(a), id(b)) print(a == b) # True, 值相同,内部 __eq__ 魔法函数 # 内部优化 小整数、小字符串 全局唯一 intern机制(下文会介绍) a1 = 1 a2 = 1 print(a1 is a2) # True,为啥id一样呢???且看下文intern机制 s1 = 'abc' s2 = 'abc' print(s1 is s2) # True class People: pass # People 全局唯一 person = People() print(type(person) is People) # 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
intern机制
在python内部有一个intern机制,会把一定范围内的小整数建立一个全局唯一对象(常量池),如上面a=1,当执行b=1时,会指向同一个对象。这个对小串的字符串来说,也是一样的。
关键词
- 内部优化
- 小整数
- 小字符串
- 全局唯一
示例代码:
a = 1 b = 1 print(a is b) print(id(a),id(b))
True 140724036245152 140724036245152
可以看到这两个对象是一致的,在python内部有一个intern机制,会把一定范围内的小整数建立一个全局唯一对象(常量池),如上面a=1,当执行b=1时,会指向同一个对象。这个对小串的字符串来说,也是一样的。
而看一下另外一段代码:
>>> a = 257 >>> b = 257 >>> a is b False
这是什么原因呢?
注意,Python仅仅对比较小的整数对象进行缓存(范围为范围[-5, 256])缓存起来,而并非是所有整数对象。需要注意的是,这仅仅是在命令行中执行,而在Pycharm或者保存为文件执行,结果是不一样的,这是因为解释器做了一部分优化。
1、is 比较两个对象的 id 值是否相等,是否指向同一个内存地址;
2、== 比较的是两个对象的内容是否相等,值是否相等;
3、小整数对象[-5,256]在全局解释器范围内被放入缓存供重复使用;
4、is 运算符比 == 效率高,在变量和None进行比较时,应该使用 is。(因为它不能重载,所以 Python 不用寻找并调用 特殊方法,而是直接比较两个整数 ID。)
python中垃圾回收的算法是采用引用计数。
# python 中垃圾回收算法为 引用计数 a = 1 # 1这个对象就会有一个计数器,a=1时会在引用计数器上+1 b = a # 引用计数器加一 del a # 引用计数器减,而不是直接回收对象,这与C++不同,Python中,当计数器=0时,才会将1对象回收,防止一直占用内存
注意:
python的del和c++中不同,c++是直接回收对象,而python del直到计数器=0时,才会对对象进行回收
Tips:
a, b 都是整型时
def add(a, b): a += b return a if __name__ == '__main__': a, b = 1, 2 c = add(a, b) print('a: %s, b: %s, c: %s' % (a, b, c)) # 结果为 a: 1, b: 2, c: 3 # a 未发生变化
a, b 都是列表时
def add(a, b): a += b return a if __name__ == '__main__': a, b = [1, 2], [3, 4] c = add(a, b) print('a: %s, b: %s, c: %s' % (a, b, c)) # 结果为 a: [1, 2, 3, 4], b: [3, 4], c: [1, 2, 3, 4] # a 发生变化!!!
a, b 都是元组时
def add(a, b): a += b return a if __name__ == '__main__': a, b = (1, 2), (3, 4) c = add(a, b) print('a: %s, b: %s, c: %s' % (a, b, c)) # 结果为 a: (1, 2), b: (3, 4), c: (1, 2, 3, 4) # a 未发生变化
默认类型为可变类型时
class Company: def __init__(self, name, staff_list=[]): self.name = name self.staff_list = staff_list def add(self, staff): self.staff_list.append(staff) def remove(self, staff): self.staff_list.remove(staff) if __name__ == '__main__': com1 = Company('com1', ['staff1', 'staff11']) com1.add('staff111') com1.remove('staff11') print(com1.staff_list) com2 = Company('com2') com2.add('staff2') com3 = Company('com3') com3.add('staff3') print(com2.staff_list) # ['staff2', 'staff3'] print(com3.staff_list) # ['staff2', 'staff3']