热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

对‘pthread_create’未定义的引用_Python对象引用,可变性和垃圾回收

变量不是盒子,而是‘便利贴’a[1,2,3]baa.append(5)a[1,2,3,5]b[1,2,3,5]变量的赋值方式
ef13c5954f80ea7bdfdb8228d9ddeca9.png

变量不是盒子,而是‘便利贴’

>>> a = [1,2,3]
>>> b = a
>>> a.append(5)
>>> a
[1, 2, 3, 5]
>>> b
[1, 2, 3, 5]

变量的赋值方式:比如x = 2是将一个变量s分配给一个对象比如整数2。而不是把整数对象2分配给变量s

>>> c = {'name':'yang','born':1997}
>>> a = c #a为c的一个别名。他们俩同时指向一个对象,'=='和'is'运算符证明这一点
>>> a == c
True
>>> a is c
True
>>> id(a),id(c)
(139644203394464, 139644203394464)
>>> a['name'] = 'yyy' #用a修改内容
>>> c #c也会被修改,因为它们俩指向的是一个对象
{'name': 'yyy', 'born': 1997}
>>> d = {'name': 'yyy', 'born': 1997} #新建一个d对象,与a和c的值一样
>>> d == a # '=='运算符比较值是否相等
True
>>> d is a #'is'运算符比较对象的标识是否相等,就是比较id()是否相等。d是新建的对象很明显不会相等
False
>>> id(d), id(a)
(139644203394536, 139644203394464)

每个变量都有标识、类型和值。对象一旦创建,他的标识绝不会变;你可以把标识理解为对象在内存中的地址。is运算符比较两个对象的标识;id()函数返回对象标识的整数表示。

元组的不可变性

#元组的不可变性其实是指tuple数据结构的物理内容(即保存的引用)不可变,与引用的对象无关。比如元组里引用了一个可变对象列表,不能改变这个引用让他变成其他字典或整数对象,但是可以修改这个可变对象的值。
>>> t1 = (1, 2, [3, 4])
>>> t2 = (1, 2, [3, 4])
>>> id(t1),id(t2)
(139644201933272, 139644201953896)
>>> t1 == t2 #值相等
True
>>> t1 is t2 #标识不相等,两个除了值相等其他完全不相关的变量
False
>>> t1[-1].append(5) #可以对元组内的列表元组进行添加操作
>>> t1 == t2 #此时他们俩的值不相等了
False

不显式的使用copy模块的deepcopy函数深复制时,都默认为浅复制

浅复制复制了最外层的容器,副本中的元素是原容器中元素的引用

>>> a = [1, 2, [3, 4]]
>>> c = a[:]
>>>a == c
True
>>> a is c #容器不一样,但是里元素的引用一样
False>>> r = (1, 2, [4,5]) #对元组或其他不可变类型对象浅复制返回的是同一个对象的引用。类似于rr = r
>>> rr = r[:]
>>> rr is r
True#浅复制后母本和副本内的元素都互为对方的标识,也就是都指向同一个对象。如果对母本或副本中的可变元素操作,因为两个引用是同一个对象,所以会影响到另一个母本或副本。但是,比如在副本中对不可变元素操作会生成一个新的对象引用,就和母本中的不可变元素不是同一个引用了,就不会影响到母本。
#下面是示例:
>>> l1 = [3, [66,55,44], (7, 8, 9)]
>>> l2 = list(l1) #对列表l1浅复制,赋值给l1
>>> l1.append(100) #l1添加一个新元素100
>>> l2
[3, [66, 55, 44], (7, 8, 9)] #l2中没有添加
>>> l1
[3, [66, 55, 44], (7, 8, 9), 100] #l1中添加成功
>>> l1[1].remove(55) #将l1[1]这个列表中的55元素删除
>>> l1
[3, [66, 44], (7, 8, 9), 100]
>>> l2
[3, [66, 44], (7, 8, 9)] #对l2也有影响,因为l2[1]这个列表和l1[1]的列表时同一个,他们两个互相时对方的别名,都指向同一个列表元素
>>> l2[1] += [1,1,1] #l2[1]就地修改列表
>>> l2
[3, [66, 44, 1, 1, 1], (7, 8, 9)]
>>> l1
[3, [66, 44, 1, 1, 1], (7, 8, 9), 100] #l1[1]也被修改,因为这是同一个对象
>>> l1[2] is l2[2] #此时l1[2]和l2[2]这两个元组是同一个对象
True
>>> l2[2] += (1,1,1) #对l2[2]这个元组添加(1,1,1)。因为元组是不可变元素,这个赋值操作不能就地添加,相当于l2[2] = l2[2]+(1,1,1),这里创建了一个新元组。
>>> l1[2] is l2[2] #此时l1[2]和l2[2]这两个元素不再是同一个对象
False
>>> l2
[3, [66, 44, 1, 1, 1], (7, 8, 9, 1, 1, 1)]
>>> l1
[3, [66, 44, 1, 1, 1], (7, 8, 9), 100] #所以这个修改并没有对l1起作用>>> l2[1] is l1[1] #可变对象就地修改,再改还是引用的同一个对象
True
>>> l1[0] is l2[0]
True

深复制

#定义一个类来测试
class Bus:def __init__(self, p=None):if p is None:p = []else:self.p = list(p)def pick(self, name):self.p.append(name)def drop(self, name):self.p.remove(name)>>> from bus import *
>>> bus1 = Bus(['a', 'b', 'cc'])
>>> bus1

>>> bus1.p
['a', 'b', 'cc']
>>> bus2 = copy.copy(bus1) #浅复制bus1
>>> bus3 = copy.deepcopy(bus1) #深复制bus1
#到此 创建了三个bus实例
>>> bus1.drop('a') #bus1的p列表中删除一个元素
>>> bus2

>>> bus2.p #bus2的p列表中也没有这个元素了,浅复制共享一个列表对象
['b', 'cc']
>>> bus3.p #深复制不会共享列表,所以不会修改
['a', 'b', 'cc']
>>> bus1.p is bus2.p
True
>>> bus1.p is bus3.p
False

函数的可变参数

#函数可能会修改接收到的任何可变对象
>>> def f(a, b):
... a += b
... print(id(a))
... return a
>>> x = [1,1]
>>> id(x)
139901112369928
>>> y = [2, 2]
>>> f(x, y) #函数的形参获得各个实参的副本,也就是说,函数内部的形参是实参的别名
139901112369928
[1, 1, 2, 2]
>>> x
[1, 1, 2, 2]
>>> x = 1 #当实参为不可变类型时
>>> y = 2
>>> id(x)
10910400
>>> f(x, y) # a += b相当于重新创建了一个a对象,上面的浅复制讲的很清楚了
10910464
3

函数的默认值是可变参数时

class Bus:def __init__(self, p=[]):self.p = pdef pick(self, name):self.p.append(name)def drop(self, name):self.p.remove(name)>>> from bus import *
>>> bus1 = Bus(['a', 'b'])
>>> bus2 = Bus()
>>> bus3 = Bus() #创建三个实例,bus3和bus3使用默认值
>>> bus1.pick('c') #bus1添加新元素
>>> bus1.p,bus2.p,bus3.p #只有bus1变了
(['a', 'b', 'c'], [], [])
>>> bus2.pick('e') #bus2添加新元素
>>> bus1.p,bus2.p,bus3.p #bus2和bus3都变了
(['a', 'b', 'c'], ['e'], ['e'])
>>> Bus.__init__.__defaults__ #这时Bus类的defaults属性已经变了
(['e'],)

上面很明显的说明了:bus2和bus3使用了参数默认值(列表对象)。默认值一是在模块加载时计算,self.p变成了p参数默认值的别名。就是说不管多少个实例,只要使用的是默认值(列表对象),那么所有实例和Bus类共享这一个列表。

函数的默认值是可变参数时的解决办法

#错误的方法
class Bus:def __init__(self, p=None):if p is None:self.p = []else:self.p = p #self.p为p的别名,他们俩都指向同一个对象。def pick(self, name):self.p.append(name)def drop(self, name):self.p.remove(name)>>> from bus import *
>>> i = ['a', 'b', 'c']
>>> b = Bus(i)
>>> b.drop('a') # b实例调用他的drop方法删除'a'的时候把i列表中的'a'也删了
>>> b.p
['b', 'c']
>>> i
['b', 'c']
#正确方法
class Bus:def __init__(self, p=None):if p is None:self.p = []else:self.p = list(p) #这时self.p是对p的一个浅复制,self.p和p指向不同对象,但是容器里面的元素还是相同的引用,如果元素为可变类型,那么还是会出现问题def pick(self, name):self.p.append(name)def drop(self, name):self.p.remove(name)
>>> from bus import *
>>> i = ['a', 'b', 'c']
>>> b = Bus(i)
>>> b.drop('a') # b实例删除'a'后i列表并没有受到影响
>>> i
['a', 'b', 'c']>>> i = ['a', 'b', [1,2]] #如果参数内元素是可变类型还是有影响
>>> b = Bus(i)
>>> b.p[2].pop() # b实例删除列表内的一个列表中的元素
2
>>> i # i列表也受到影响
['a', 'b', [1]]

总结:浅复制复制的是最外层的容器,里面的元素还是原容器中元素的引用,也就是修改里面的可变元素两个容器都会受到影响。深复制相当于重新创建了一个对象,里面的元素和原容器一点关系都没有。

成长离不开与优秀的同伴共同交流,如果你需要好的学习环境,好的学习资源,这里欢迎每一位热爱Python的小伙伴,Python学习圈

垃圾回收

在cpython中,垃圾回收的主要算法是引用计数。每个对象都会统计有多少引用指向自己。当计数归零时对象就立即被销毁。当然python还有其他更复杂的垃圾回收算法,而且不依赖引用计数。

>>> import weakref
>>> s1 = {1,2,3}
>>> s2 = s1 #s2 is s1。指向同一个集合
>>> def a():
... print('aaa')
>>> end = weakref.finalize(s1, a) # weakref是一个弱引用包。这里在s1引用对象上注册a回调
>>> end

>>> del s1
>>> end.alive #对象还没有被销毁
True
>>> s2 = 2 # 让s2指向其他对象,此时没有对那个集合的引用。对象被销毁执行回调函数输出'aaa'
aaa




推荐阅读
  • Zabbix自定义监控与邮件告警配置实践
    本文详细介绍了如何在Zabbix中添加自定义监控项目,配置邮件告警功能,并解决测试告警时遇到的邮件不发送问题。 ... [详细]
  • AI炼金术:KNN分类器的构建与应用
    本文介绍了如何使用Python及其相关库(如NumPy、scikit-learn和matplotlib)构建KNN分类器模型。通过详细的数据准备、模型训练及新样本预测的过程,展示KNN算法的实际操作步骤。 ... [详细]
  • Web动态服务器Python基本实现
    Web动态服务器Python基本实现 ... [详细]
  • 在OpenCV 3.1.0中实现SIFT与SURF特征检测
    本文介绍如何在OpenCV 3.1.0版本中通过Python 2.7环境使用SIFT和SURF算法进行图像特征点检测。由于这些高级功能在OpenCV 3.0.0及更高版本中被移至额外的contrib模块,因此需要特别处理才能正常使用。 ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • 将字符串中的嵌套列表转换回嵌套列表 ... [详细]
  • RTThread线程间通信
    线程中通信在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取& ... [详细]
  • 本文探讨了Python类型注解使用率低下的原因,主要归结于历史背景和投资回报率(ROI)的考量。文章不仅分析了类型注解的实际效用,还回顾了Python类型注解的发展历程。 ... [详细]
  • 本文将详细探讨 Python 编程语言中 sys.argv 的使用方法及其重要性。通过实际案例,我们将了解如何在命令行环境中传递参数给 Python 脚本,并分析这些参数是如何被处理和使用的。 ... [详细]
  • 本文介绍了多维缩放(MDS)技术,这是一种将高维数据映射到低维空间的方法,通过保持原始数据间的关系,以便于可视化和分析。文章详细描述了MDS的原理和实现过程,并提供了Python代码示例。 ... [详细]
  • JUnit下的测试和suite
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 问题场景用Java进行web开发过程当中,当遇到很多很多个字段的实体时,最苦恼的莫过于编辑字段的查看和修改界面,发现2个页面存在很多重复信息,能不能写一遍?有没有轮子用都不如自己造。解决方式笔者根据自 ... [详细]
  • 本文介绍如何在阿里云环境中利用 Docker 容器化技术部署一个简单的 Flask Web 应用,并确保其可通过互联网访问。内容涵盖 Python 代码编写、Dockerfile 配置、镜像构建及容器运行等步骤。 ... [详细]
  • 本文介绍了如何将Spring属性占位符与Jersey的@Path和@ApplicationPath注解结合使用,以便在资源路径中动态解析属性值。 ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom"echarts";4、如果用到map(地图),还 ... [详细]
author-avatar
mobiledu2502889953
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有