Python番外篇:变量是盒子还是标签

引言

前面通过几十篇文章,大概把Python的一些比较实用的基础做了一些介绍,学会这些,基本能应付日常的小的需求开发了,写一些小工具,提高工作的处理效率。

接下来,准备开始进入一个新的篇章,也就是Python的面向对象了。

但是,在开始介绍Python面向对象之前,还是需要对变量这个从开始学一直到每次写代码都要用到的概念,再稍微唠叨一番。

因为,很多编程自学者,哪怕写了挺多的代码了,其实对变量还是一知半解的状态。

变量的盒子比喻

有些教材在介绍变量是,经常将变量比喻为盒子,变量的值,为盒子中所保存的内容。这对于静态语言中的普通变量来说,是合适的。但是,对理解面向对象语言中的引用型变量,会造成理解上的困扰。

如果变量是盒子,那以下的代码是不可解释的:

# 如果变量是盒子,现在有一个盒子叫a,这个盒子里面装着[1, 2, 3]
a = [1, 2, 3]
# 有一个新的盒子,叫b,这个盒子里面也装着[1, 2, 3]
b = a
# 目前来说,从print输出来看,盒子的比喻都是没有问题的
print(a)
print(b)
# 但是,当我们修改了b[0]时,盒子的比喻无法解释了
b[0] = 100
print(b)
# 我们只是修改了b盒子的元素,为什么a盒子的也变了
print(a)

执行结果:

02f35c4b645ff8ff0c42b3cb993e2084.jpeg

变量的标签比喻

既然变量是盒子的比喻,解释不通了,我们对于变量的理解,必须换一个思路去理解。

在Python中一切皆是对象,变量是跟对象进行绑定的,这种绑定关系又是可以改变的,也许把变量理解成贴在对象上的便签纸,也许更加容易理解。

e0f74cfce290e351ec770355704baadd.jpeg

注:标签比喻及对应的图片来源于《流畅的Python》。

垃圾就该待在垃圾箱里

我们在Python代码中,使用任何对象,都是通过变量来实现的。一个对象上有一个变量与之对应,也就是贴了一个标签,在Python中叫做存在一个对该对象的引用。有多个变量与这同一个对象产生了对应,也就是有多个对该对象的引用。

那么,问题来了,如果一个内存中的对象上没有贴任何标签,也就是没有任何变量与之对应,也就是对该对象的引用数为0,那么这个对象我们可以使用吗?

答案显然是否定的,因为我们根本无法找到这个对象,那么这个对象在内存中,占用着存储空间,却没有任何用,那就是内存垃圾了,垃圾就应该待在垃圾箱里。

所以,最基础、最朴素的内存回收就是基于对象的引用计数实现的,当一个对象的引用计数为0,则这个对象被标记为垃圾,在特定的时机,会出发垃圾回收机制,将之从内存中释放。

在Python中,可以通过sys.getrefcount()函数,获取一个对象的引用,但是,需要注意的是:getrefcount()函数返回的计数会比实际引用计数多一个,因为它包含了作为参数传递给getrefcount()函数时创建的临时引用。

import sys
a = [1, 2, 3]
print(sys.getrefcount(a))
b = a
print(sys.getrefcount(a))
c = a
print(sys.getrefcount(a))
# del操作,并不是删除对象,而是解除变量对对象的引用
del c
print(sys.getrefcount(a))
# 重新赋值操作,也会解除对原对象的引用
b = 123
print(sys.getrefcount(a))

执行结果:

dfa11f99aa9ef3c5b98f41f59752815b.jpeg

通过引用计数,我们可以知道,此前对del操作的理解,其实可能也是有些偏差的。

del操作,只是解除该变量与某个对象对应的引用关系,对象的引用计数会减少,但并不是字面意思上的删除对象。即使当前变量是对某个对象的最后一个引用了,del之后引用计数为0了,也不是立马进行垃圾回收。而是,在恰当的时机,比如定时或者内存空间不足时,才会触发垃圾回收。

而且,垃圾回收并不是必须的(如果内存足够),垃圾回收必须满足的一个质量保证是,不能将还在使用的对象当做垃圾进行回收。

对象的比较

既然变量本身是对对象的引用,两个变量的比较,其本质是对引用对象的比较。

关于比较,在编程中有两种,一种叫同一性比较,一种叫相等性比较。

同一性比较,是判断两个变量是不是对同一个对象的两个引用、两个标签而已,同一性,显然是隐含相等性的。

相等性比较,是判断两个对象的值/内容是否相等。

a = [1, 2, 3]
b = a
c = [1, 2, 3]
# 同一性比较,通过is实现
print(a is b)
print(a is c)# 相等性比较,通过==实现
print(a == b)
print(a == c)

执行结果:

2d06803fb6b916e44673d460ac51fe6e.jpeg

==进行的相等性判断,其实是Python中的语法糖,本质上是通过__eq__()魔法函数来实现的。所以,如果是自定义的对象,可以通过实现__eq__()函数,来实现自定义相等性的语义判断。

总结

本文将变量与对象的关系,做了进一步的解析,同时稍微提及了关于Python中的对象引用计数与垃圾回收的概念,并对对象的两种比较方式进行了简单的解释说明。

a0dd7f9e3a8f30048d1c4a00d98d1334.jpeg

在接下来的文章中,我们将进入Python新的篇章“面向对象”的介绍。

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

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

相关文章

C#如何引用dll动态链接库文件的注释

1、dll动态库文件项目生成属性中要勾选“XML文档文件” 注意:XML文件的名字切勿修改。 2、添加引用时XML文件要与DLL文件在同一个目录下。 3、如果要是添加引用的时候XML不在相同目录下,之后又将XML文件复制到相同的目录下,需要删除引用&am…

当设计模式牵手LLM

模版方法模式 何为模版设计模式 想象一下 如果我们要泡一杯茶 我们要循序渐进地 煮水温杯注水浸茶茶水入杯加点配料 如此,泡茶的工序就完成了,那么模板方法模式,相信各位也有了一定的概念:定义了一个算法的骨架,而…

UDP的报文结构及其注意事项

1. 概述 UDP(User Datagram Protocol)是一种无连接的传输层协议,它提供了一种简单的数据传输服务,不保证数据的可靠传输。在网络通信中,UDP通常用于一些对实时性要求较高、数据量较小、传输延迟较低的应用&#xff0c…

【JVM基础07】——类加载器-什么是类加载器?类加载器有哪些?双亲委派了解吗?

目录 1- 引言:类加载器1-1 类加载器是什么?(What)1-2 为什么要用类加载器? 作用:类加载的过程?(Why) 2- ⭐核心:类加载器详解(How)2-1 类加载器分类2-2 什么是双亲委派模型?2-3 为什么采用双亲委…

Pytorch基础:Tensor的squeeze和unsqueeze方法

相关阅读 Pytorch基础https://blog.csdn.net/weixin_45791458/category_12457644.html?spm1001.2014.3001.5482 在Pytorch中,squeeze和unsqueeze是Tensor的一个重要方法,同时它们也是torch模块中的一个函数,它们的语法如下所示。 Tensor.…

【SpringBoot】1 Gitee

本项目 Gitee 地址:https://gitee.com/Lin_DH/system idea中可能装个gitee的插件,这样操作起来比较方便。 1)登录 Gitee 官网(https://gitee.com/),新建仓库。 2)复制新建的 Gitee 仓库地址&am…

Unity3D之TextMeshPro使用

文章目录 1. TextMeshPro简介2. TextMeshPro创建3. TextMeshPro脚本中调用4. TextMeshPro字体设置及中文支持过程中出现的一些问题 1. TextMeshPro简介 【官网文档】https://docs.unity.cn/cn/2020.3/Manual/com.unity.textmeshpro.html TextMeshPro 是 Unity 的最终文本解决…

软件测试---Linux

Linux命令使用:为了将来工作中与服务器设备进行交互而准备的技能(远程连接/命令的使用)数据库的使用:MySQL,除了查询动作需要重点掌握以外,其他操作了解即可什么是虚拟机 通过虚拟化技术,在电脑…

Leetcode49. 字母异位词分组(java实现)

今天我来给大家分享的是leetcode49的解题思路,题目描述如下 如果没有做过leetcode242题目的同学,可以先把它做了,会更好理解异位词的概念。 本道题的大题思路是: 首先遍历strs,然后统计每一个数组元素出现的次数&#…

电商数据精细化运营解决方案(18页PPT)

方案介绍: 电商数据精细化运营解决方案通过全面、深入的数据分析与应用,助力电商企业实现精细化管理和精准化营销,从而在激烈的市场竞争中脱颖而出。 部分方案内容:

Prometheus 监控Tomcat等java应用的状态

5月应用服务出现问题,当别的小伙伴问我,有没有Tomcat等应用状态的监控的时候,我有点儿尴尬。所以赶紧抽空部署一下。 在配置之前,就当已经会安装jdk和tomcat了。 一、下载jmx_exporter #linux下 cd /usr/local/prometheus wget …

Debug-018-elementUI-el-tree中通过CSS隐藏任意一项的选择框checkbox

前情提要: 我们项目中使用的是elementUI,业务中经常需要使用到el-tree组件去实现一些有层级关系的功能。现在有一个需求描述一下:首先是这个el-tree是个有checkbox的树,每一子节点都可以被选择,用于去实现一些系统的权…

PHP多场地预定小程序系统源码

一键畅游多地!多场地预定小程序的超实用指南 段落一:【开篇:告别繁琐,预订新体验】 🎉🚀 还在为多个活动或会议的场地预订而头疼不已吗?多场地预定小程序来拯救你啦!它像是一位贴心…

【QT】QT 窗口(菜单栏、工具栏、状态栏、浮动窗口、对话框)

Qt 窗口是通过 QMainWindow类来实现的。 QMainWindow 是一个为用户提供主窗口程序的类,继承自 QWidget 类,并且提供了⼀个预定义的布局。QMainWindow 包含一个菜单栏(Menu Bar)、多个工具栏(Tool Bars)、…

7.26总结

1.我发现我的界面非常不符合要求,魔改了一下界面 2.在此基础上实现了编辑资料的功能,之前的编辑资料不够用完善,现在将所有数据存入数据库, 然后将更改的图片路径存到了服务端的文件夹,文件名为id更方便获取图片&…

Cuda编程模型中常见的错误检测方法

Cuda编程模型中常见的错误检测方法 1 CUDA错误检测简介2 直接嵌入检测函数2.1 检测函数介绍2.2 使用示例 3 封装在.cuh头文件中嵌入3.1 创建 error.cuh 头文件3.2 在 CUDA 程序中包含 error.cuh 并调用 CHECK 宏3.3 使用示例 1 CUDA错误检测简介 CUDA编程模型中的错误检测是确…

C++ - char*、const char*、char[]、string

const char* const char* 用来定义字符串常量。 char[ ] char型的字符数组是一种定长的数组,存储指定长度的字符序列,数组中的每个元素都是一个char类型的变量,如: char arr[] {h, a, l, l, o, \0}; char c arr[0]; // 访问…

【二叉树 C++DFS】2458. 移除子树后的二叉树高度

本文涉及知识点 二叉树 CDFS LeetCode 2458. 移除子树后的二叉树高度 给你一棵 二叉树 的根节点 root ,树中有 n 个节点。每个节点都可以被分配一个从 1 到 n 且互不相同的值。另给你一个长度为 m 的数组 queries 。 你必须在树上执行 m 个 独立 的查询&#xff…

模式Hash和history

vuerouter有两种路由模式Hash和history。区别:Hash为默认模式,url中包含一个#符号的哈希部分。优势:兼容性好,不需要后端服务器的特殊配置。缺点:不够美观,搜索引擎优化较差。History模式使用的浏览器的His…

C# 写入SQLServer数据库报错SqlException: 不能将值 NULL 插入列 ‘ID‘

private int id; [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]//id自增 public int ID { get > id; set > id value; } 将ID属性下的标识规范由否改成是