ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Advance Python 06 :对象引用、可变性和垃圾回收

2022-02-08 17:58:00  阅读:202  来源: 互联网

标签:__ Advance 06 对象 python print 可变性 id staff


Introduce

  • python 的变量是什么

  • is 和 == 区别

  • del 语句和垃圾回收

  • 一个经典的参数错误

文章目录

一、python 的变量是什么

1.1、变量、内存的理解

变量:用来标识(identify)一块内存区域。为了方便表示内存,我们操作变量实质上是在操作变量指向的那块内存单元。编译器负责分配。我们可以使用Python内建函数id()来获取变量的地址

变量名:是一个标识符(dientify),用来代之一块内存空间,使用这个变量名,我们可以很方便的操作这块内存区域。

内存: 内存是我们电脑硬件,用来存放数据,形象的理解就是内存有一个一个的小格子组成,每个格子的大小是一个字节,每个格子可以存放一个字节大小的数据。我们如何才能知道,数据存放在哪些格子中,那就得靠地址,地址类似于楼房的门牌号,是内存的标识符。

1.2、id()

id(object)函数是返回对象object在其生命周期内位于内存中的地址,id函数的参数类型是一个对象。

1.3、python 的变量是什么

概述

  • 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  

过程 :

  • 先到内存中申请一块int空间
  • 把 a 贴在这块内存空间上

便利贴的大小是固定的, 所以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 和 == 区别

2.1、概述

  • 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

2.2、魔法函数__eq__

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

2.3、intern机制

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时,会指向同一个对象。这个对小串的字符串来说,也是一样的。

2.4、意外

而看一下另外一段代码:

>>> a = 257
>>> b = 257
>>> a is b
False

这是什么原因呢?

注意,Python仅仅对比较小的整数对象进行缓存(范围为范围[-5, 256])缓存起来,而并非是所有整数对象。需要注意的是,这仅仅是在命令行中执行,而在Pycharm或者保存为文件执行,结果是不一样的,这是因为解释器做了一部分优化。

2.5、总结

1、is 比较两个对象的 id 值是否相等,是否指向同一个内存地址;
2、== 比较的是两个对象的内容是否相等,值是否相等;
3、小整数对象[-5,256]在全局解释器范围内被放入缓存供重复使用;
4、is 运算符比 == 效率高,在变量和None进行比较时,应该使用 is。(因为它不能重载,所以 Python 不用寻找并调用 特殊方法,而是直接比较两个整数 ID。)

三、del 语句和垃圾回收

python中垃圾回收的算法是采用引用计数。

# python 中垃圾回收算法为 引用计数

a = 1  # 1这个对象就会有一个计数器,a=1时会在引用计数器上+1
b = a  # 引用计数器加一
del a  # 引用计数器减,而不是直接回收对象,这与C++不同,Python中,当计数器=0时,才会将1对象回收,防止一直占用内存
  • del 触发 del 逻辑
  • 对象引用计数为 0 时,会被垃圾回收

注意:

​ python的del和c++中不同,c++是直接回收对象,而python del直到计数器=0时,才会对对象进行回收

Tips:

  • 在cpython2.0中就不是计数器。
  • 当然,自己定义一个对象时,可以在__del__中写自己的垃圾回收逻辑

四、一个经典的参数错误

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']
    
  • 默认值为可变类型 [],com2.staff_list 和 com3.staff_list 指向一块内存空间

标签:__,Advance,06,对象,python,print,可变性,id,staff
来源: https://blog.csdn.net/qq_55535816/article/details/122828663

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有