流畅的Python(八)-对象引用、可变性和垃圾回收

一、核心要义

本章主要讨论对象对象名称之间的区别。名称不是对象,而是单独的东西。

二、代码示例

1、标识、相等性和别名

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 10:58
# @Author  : Maple
# @File    : 01-标识,相等性和别名.py
# @Software: PyCharmp1 = {'name':'maple','gender':'male','age':32}
p2 = p1
p3 ={'name':'maple','gender':'male','age':32}if __name__ == '__main__':# 1.p1所指向对象的标识(CPython中指对象的内存地址)print(id(p1)) # 1404688094144print('*******************************')# 2.p1和p2指向同一个对象,p2也被称作p1的别名print(id(p2))# 1404688094144print(id(p1) == id(p2)) # Trueprint('*******************************')# 3.p3虽然和p1的值完全相同,但其实指向不同的对象# 值相等print(p1 == p3) # True# 但其实指向不同的对象print(id(p3)) # 1404688094208print(id(p3) == id(p1)) # False# 实际开发很少使用id方式判断是否 同一个对象,而是使用isprint(p3 is p1) # False

2、元组的相对不可变性

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 11:05
# @Author  : Maple
# @File    : 02-元组的相对不可变性.py
# @Software: PyCharm"""
元组的不可变性是指tuple数据结构的物理内容(即保存的引用)不可变,但如果引用 的某个可变对象本身发生了变化
,元组的值其实也会发生变化
"""# 元组中的第三个元素[30,40]是可变对象-列表
t1 = (1,2,[30,40])
t2 = (1,2,[30,40])if __name__ == '__main__':# 1. 最开始,t1和t2的值是相等的print(t1 == t2) # True# 2.修改t1中第三个元素的值## 修改前列表的标识print(id(t1[-1])) # 2250527148480t1[-1].append(99)print(t1[-1]) # [30, 40, 99]## 修改后列表的标识(与修改前相同,也就是引用本身 并不会发生变化)print(id(t1[-1])) # 2250527148480## 但是引用 所对应的值很显然已经发生了变化print(t1[-1] == t2[-1]) # False

3、浅拷贝

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 11:21
# @Author  : Maple
# @File    : 03-浅拷贝.py
# @Software: PyCharm"""
浅拷贝:副本中的元素是源容器中元素的引用
"""l1 = [3,[66,55,44],(7,8,9)]
# l2是l1的浅拷贝
l2 = list(l1)if __name__ == '__main__':#0. l1和l2是不同的对象print(l1 is l2 ) # False# 但是l1和l2中各个元素的引用都是指向同一个对象for i in range(len(l1)):print(l1[i] is l2[i]) # True,True,True# 1. l1中追加100,对l2没有影响l1.append(100)print('l1:',l1,';l2:',l2) # l1: [3, [66, 55, 44], (7, 8, 9), 100] ;l2: [3, [66, 55, 44], (7, 8, 9)]#2. l1中第一个元素修改为30,对l2没有影响,因为对数值进行修改,会直接生成一个新的对象,l1[0] = 30print('l1:', l1, ';l2:', l2) # l1: [30, [66, 55, 44], (7, 8, 9), 100] ;l2: [3, [66, 55, 44], (7, 8, 9)]#3. l1第二个元素移除55,l2会同步移除l1[1].remove(55)print('l1:', l1, ';l2:', l2) # l1: [30, [66, 44], (7, 8, 9), 100] ;l2: [3, [66, 44], (7, 8, 9)]#4. l2中第二个元素增加[33,22],因为list是可变对象,+=操作会就地改变对象本身(可参考第二章`拼接和增量操作`部分)#因此l1中的第二个也会同步发生变化l2[1] += [33,22]print('l1:', l1, ';l2:', l2) #l1: [30, [66, 44, 33, 22], (7, 8, 9), 100] ;l2: [3, [66, 44, 33, 22], (7, 8, 9)]#5. l2中第三个元素增加(10,11),因为元组是不可变对象,+=操作会生成一个新的对象(可参考第二章`拼接和增量操作`部分)#因此不会影响l1中的对应值l2[2] +=(10,11)print('l1:', l1, ';l2:', l2) #l1: [30, [66, 44, 33, 22], (7, 8, 9), 100] ;l2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

补充:l1和l2的内存图:

(1)最初状态

(2)最终状态

4、深拷贝

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 11:44
# @Author  : Maple
# @File    : 04-深拷贝.py
# @Software: PyCharmimport copyclass Bus:def __init__(self,passengers = None):if passengers is None:self.passengers = []else:self.passengers = passengersdef pick(self,name):self.passengers.append(name)def drop(self,name):self.passengers.remove(name)if __name__ == '__main__':bus1 = Bus(['Maple','Alice','Carry'])# bus1的浅拷贝bus2 = copy.copy(bus1)#bus1的深拷贝bus3 = copy.deepcopy(bus1)# 首先bus1,bus2和bus3是不同的对象print(id(bus1),',',id(bus2),',',id(bus3)) # 1973781178160 , 1973781642592 , 1973781754688# 但是由于bus2是bus1的浅拷贝,所以bus1.passengers和bus2.passengers指向同一个对象,而bus3.passengers则指向另外一个对象print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)) # 1973781759040 1973781759040 1973781758912# 从bus1.passengers中移除Maple,bus1.passengers也会发生相应变化bus1.drop('Maple')print(bus2.passengers) # ['Alice', 'Carry']# bus3.passengers 不受影响print(bus3.passengers) # ['Maple', 'Alice', 'Carry']

5、函数参数引用

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 13:08
# @Author  : Maple
# @File    : 05-函数参数引用.py
# @Software: PyCharml1 = [3,[66,55,44],(7,8,9)]def f(a):"""函数形参是实参的别名,即两者指向同一个对象"""return id(a)def f2(a,b):# 形参a是实参的一个副本print('局部变量a运算前:',id(a))a +=bprint('局部变量a运算后:',id(a))return aif __name__ == '__main__':# 1. 形参和实参指向同一个对象print(id(l1)) #1596413019840print(f(l1)) #1596413019840print('************************')# 2. 数值类型测试a1 = 1b1 = 2print('全局变量a:',id(a1)) #  140724639640480""" 局部变量a运算前: 140724639640480局部变量a运算后: 140724639640544形参a返回值: 3"""print('形参a返回值:',f2(a1,b1)) # 3: 形参a指向一个新的对象(对于数值类型,+运算会生成一个新的对象)print(a1) # 1:实参a1并不会发生变化print('*******************************')# 3.列表类型测试a2 = [1,2,3]b2 = [4]print('全局变量a:', id(a2))  # 2096208446016""" 局部变量a运算前: 2096208446016局部变量a运算后: 2096208446016形参a返回值: [1, 2, 3, 4]"""print('形参a返回值:', f2(a2, b2))  # 3: 形参a就地修改原对象(对于列表类型,+运算会就地修改对象本身)print(a2)  # [1, 2, 3, 4]:实参a2也会同步发生变化print('*******************************')# 4.元组测试a3 = (1, 2, 3)b3 = (4,)print('全局变量a:', id(a3))  # 3147866564096""" 局部变量a运算前: 3147866564096局部变量a运算后: 3147866469536形参a返回值: (1, 2, 3, 4)"""print('形参a返回值:', f2(a3, b3))  # 3: 形参a指向一个新的对象(对于元组类型,+运算会生成一个新的对象)print(a3)  # (1, 2, 3):实参a3并不会发生变化

6、使用不可变对象作为函数参数默认值

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 14:35
# @Author  : Maple
# @File    : 06-使用不可变对象作为函数参数默认值.py
# @Software: PyCharm"""
函数参数的默认值应该尽量选择不可变类型,如果使用可变类型(比如list)可能会存在一些隐患
"""class Bus:# passengers默认值使用可变类型的listdef __init__(self,passengers = []):# self.passengers 是passengers的一个别名,即两者是同一个对象# 同时passengers又是空列表的一个别名# 而空列表作为默认值,会在函数定义时加载的,也就是默认值会变成函数对象的一个属性self.passengers = passengersdef pick(self,name):self.passengers.append(name)def drop(self,name):self.passengers.remove(name)if __name__ == '__main__':# 查看对象的默认属性:此时还是默认属性值还不包括Mapleprint(Bus.__init__.__defaults__)bus1 = Bus()bus1.pick('Maple')# 再次查看对象的默认属性,因为函数又一次加载了,同时在bus1.pick中已经添加了一个元素,所以默认属性值中会有Mapleprint(Bus.__init__.__defaults__)bus2 = Bus()# bus2的passengers竟然有值,明明初始化是空,原因分析# 1. bus1创建时,self.passengers指向 空列表对象,同时往该对象添加元素'Maple'# 2. bus2创建时,self.passengers指向 同一个`空列表`对象,只不过此时对象(默认属性)里已经有了值:Mapleprint(bus2.passengers) # ['Maple']

7、防御可变参数

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 14:53
# @Author  : Maple
# @File    : 07-防御可变参数.py
# @Software: PyCharmclass TwilightBus:"""篮球队成员从 bus下车后,竟然同时从篮球队消失,不符合常理"""def __init__(self,basketball_team):if basketball_team is None:self.basketball_team = []else:self.basketball_team = basketball_teamdef pick(self,member):self.basketball_team.append(member)def drop(self,member):self.basketball_team.remove(member)class TwilightBus2:def __init__(self,basketball_team):if basketball_team is None:self.basketball_team = []else:# 创建basketball_team的副本(浅拷贝) 赋值给self.basketball_teamself.basketball_team = list(basketball_team)def pick(self,member):self.basketball_team.append(member)def drop(self,member):self.basketball_team.remove(member)if __name__ == '__main__':#1. TwilightBus测试basketball_team = ['Maple','Kelly','Jack']bus = TwilightBus(basketball_team)bus.drop('Maple')# Maple从bus下车后,连带从basketball_team中消息了,为何?# 1. self.basketball_team和 basketball_teams 指向同一个对象(互为别名),#     因此针对 self.basketball_team的操作,会影响basketball_teams的值# 解决方式是创建 basketball_teams的副本,让针对self.basketball_team的操作不会影响到basketball_teamsprint(basketball_team) # ['Kelly', 'Jack']#2. TwilightBus2测试basketball_team2 = ['Maple', 'Kelly', 'Jack']bus2 = TwilightBus2(basketball_team2)bus2.drop('Maple')print(basketball_team2)  # ['Maple', 'Kelly', 'Jack']

八、del和弱引用

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/8 15:06
# @Author  : Maple
# @File    : 08-del和弱引用.py
# @Software: PyCharm"""
1.del删除变量名称而已,并不会直接删除对象
2.当对象的引用计数变为0时,对象立即就被销毁
"""import weakrefs1 = {1,2,3}
s2 = s1
def bye():print('随风而逝....')a_set = {0,1}if __name__ == '__main__':ender = weakref.finalize(s1,bye)print(ender.alive) # True# 虽然删除了s1,但{1,2,3}仍然还剩下s2这个引用del s1# 所以ender仍然aliveprint(ender.alive) # True# s2指向一个新的对象,{1,2,3}的引用计数变为0s2 = 'spam'# 因此ender.alive变为Falseprint(ender.alive) # 随风而逝..../False# 遗留一个问题,按理说函数形参 也会指向{1,2,3},所以当s1和s2被删除后,{1,2,3}应该还剩一个引用# 但因为 形参是对{1,2,3}的弱引用,并不会占用引用计数"""控制台会话:Python控制台会自动把_变量绑定到结果不为None的表达式结果上>>>import weakref>>>a_set = {0,1}# wref即是对a_set的弱引用>>>wref = weakref.ref(a_set)# 返回弱引用对象的值:即{0,1},此时系统会自动分配一个变量_指向{0,1}>>>wref(){0, 1}# a_set重新指向一个新的对象(注意此时仍然有一个变量_ 指向{0,1},所以{0,1}并不会被销毁)>>>a_set = {1}# 仍然是{0,1}>>>wref(){0, 1}# 执行完,系统自动分配的变量指向False,因此没有变量指向{0,1},该对象会被销毁,之后wref()也就为None了>>>wref() is NoneFalse>>>wref() is NoneTrue"""

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/462060.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大型秒杀中如何减库存?JAVA 架构知识

目前来看,业务系统中最常见的就是预扣库存方案,像你在买机票、买电影票时,下单后一般都有个“有效付款时间”,超过这个时间订单自动释放,这都是典型的预扣库存方案。而具体到秒杀这个场景,应该采用哪种方案…

《剑指 Offer》专项突破版 - 面试题 30 和 31:详解如何设计哈希表以及利用哈希表设计更加高级、复杂的数据结构

目录 一、哈希表的基础知识 二、哈希表的设计 2.1 - 插入、删除和随机访问都是 O(1) 的容器 2.2 - 最近最少使用缓存 一、哈希表的基础知识 哈希表是一种常见的数据结构,在解决算法面试题的时候经常需要用到哈希表。哈希表最大的优点是高效,在哈希表…

Python算法题集_搜索二维矩阵II

Python算法题集_搜索二维矩阵II 题41:搜索二维矩阵II1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【双层循环】2) 改进版一【行尾检测】3) 改进版二【对角线划分】 4. 最优算法 本文为Python算法题集之一的代码示例 题41&#xf…

政安晨:政安晨:机器学习快速入门(三){pandas与scikit-learn} {模型验证及欠拟合与过拟合}

这一篇中,咱们使用Pandas与Scikit-liarn工具进行一下模型验证,之后再顺势了解一些过拟合与欠拟合,这是您逐渐深入机器学习的开始! 模型验证 评估您的模型性能,以便测试和比较其他选择。 在上一篇中,您已经…

网络请求库axios

一、认识Axios库 为什么选择axios? 功能特点: 在浏览器中发送 XMLHttpRequests 请求在 node.js 中发送 http请求支持 Promise API拦截请求和响应转换请求和响应数据 补充: axios名称的由来? 个人理解没有具体的翻译. axios: ajax i/o system 二、axios发送请求 1.axios请求…

github拉取项目,pycharm配置远程服务器环境

拉取项目 从github上拉取项目到pycharmpycharm右下角选择远程服务器上的环境 2.1. 如图 2.2. 输入远程服务器的host,port,username,password连接 2.3. 选择服务器上的环境 链接第3点 注:如果服务器上环境不存在,先创建…

Qt简易登录界面

代码: #include "mywidget.h" #include "ui_mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget) {ui->setupUi(this);ui->background->setPixmap(QPixmap(":/qt picture/logo.png"))…

单片机学习笔记---串口向电脑发送数据电脑通过串口控制LED

目录 串口向电脑发送数据 每隔一秒串口就发送一个递增的数给电脑 电脑通过串口控制LED 波特率的具体计算 HEX模式和文本模式 前两节是本节的理论基础,这节开始代码演示! 串口向电脑发送数据 接下来先开始演示一下串口单向发送一个数字给电脑&…

【学习笔记】TypeScript编译选项

TS 中的编译选项 我们写了一个TS的文件,我们需要使用如下的命令将我们的TS文件转换为JS文件。 tsc xxx.ts 这样会产生一个对应的js文件 自动编译文件 编译文件时,使用 -W 指令后,TS编译器会自动监视文件的变化,并在文件发生变…

[HTTP协议]应用层的HTTP 协议介绍

目录 1.前言 2.使用fiddler抓包来观察HTTP协议格式 3.HTTP协议的基本格式 2.1请求 2,1.1首行 2.1.2请求头 2.1.3空行 2.2响应 2.2.1首行 2.2.2响应头 键值对 ​编辑2.2.3空行 2.2.4载荷(响应正文) 3.认识URL 3.1关于URL encode 1.前言 我们在前面的博客中,简单的…

windows编程-系统编程入门

1.进程线程概念(简略版) 1.1 进程 1.1.1 概念 我们编写的代码只是一个存储在硬盘的静态文件,通过编译后就会生成二进制可执行文件,当我们运行这个可执行文件后,它会被装载到内存中,接着 CPU 会执行程序中…

【C++】初识模板:函数模板和类模板

目录 一、模板函数 1、函数模板的概念 2、函数模板的格式 3、函数模板的原理 4、函数模板实例化 5、 模板参数的匹配原则 二、类模板 1 、类模板的定义格式 2 、类模板的实例化 3、模板类示例 一、模板函数 1、函数模板的概念 函数模板代表了一个函数家族&#xff0c…