Python 变量?对象?引用?赋值?一个例子解释清楚

简介: Python 变量?对象?引用?赋值?一个例子解释清楚

哈喽大家好,我是咸鱼。

前天有个小伙伴找到我,给了我一段 python 代码:

a = [1, 2]
a[1] = a
print(a[1])

然后问我为什么结果是 [1, [...]],我一看这个问题有意思,我说三言两语解释不清楚,我写篇文章到时候你看下吧,于是有了今天这篇文章。

在正式开始之前,让我们先弄清楚一些概念。

对象?变量?引用?赋值?

"Python 中一切皆对象",相信这句话大家在学习 Python 的时候都已经听的耳朵起茧子了吧。

在 Python 中,所有的数据都是对象,包括基本数据类型(例如整数、浮点数、字符串等)以及用户自定义的类型(类的实例等)。

对象其实是内存中分配的一块空间,用来存储其值。每个对象都有一个唯一的标识符(id),可以通过 id() 函数获取

不但如此,每一个对象都具有两个标准的头部信息:

  • 类型标志符(Type Identifier): 每个对象都有一个类型信息,可以通过 type() 函数获取。
  • 引用计数器(Reference Counter): 引用计数器表示有多少个引用指向该对象,当引用计数降为零时,对象会被垃圾回收。( Python 也使用其他垃圾回收机制,例如循环垃圾回收器来处理引用环的情况。)

image-20240124095856004.png

在 Python 中,变量实际上是对象的【引用】,而不是对象本身的【存储】。当我们执行赋值语句时,会自动建立变量和对象之间的关系,即引用。

变量就像是一个指针,【指向】内存中存储对象的位置。

我们来看一个例子:

a = 1
b = a
a = a + 1

首先将 1 赋值于 a,即 a 指向了 1 这个对象。
image-20240124100655581.png

接着 b = a 则表示让变量 b 也同时指向 1 这个对象。Python 的对象可以被多个变量所指向(引用)。

image-20240124100805326.png

最后执行 a = a + 1,在这里需要注意的是,Python 的基础数据类型(例如整型(int)、字符串(string)等)是不可变的

所以,a = a + 1,并不是让 a 的值增加 1,而是表示重新创建了一个新的值为 2 的对象,并让 a指向它。但是 b 仍然不变,仍然指向 1 这个对象。

因此最后的结果是,a 的值变成了 2,而 b的值不变仍然是 1

image-20240124174227765.png

通过这个例子你可以看到,这里的 ab,开始只是两个指向同一个对象的变量而已,或者你也可以把它们想象成同一个对象的两个名字。

简单的赋值 b = a,并不表示重新创建了新对象,只是让同一个对象被多个变量指向或引用。

为什么?

在了解了变量、对象、引用、赋值之后,我们回到一开始的例子。

a = [1, 2]
a[1] = a
print(a[1])

这段代码中创建了一个列表 a,其中包含两个元素(1 和 2),然后 a[1] 被赋值为整个列表 aa[1] = a),当你打印 a[1] 时,它实际上是指向列表 a 本身。

a = [1, <reference to a>]

这样就会导致循环引用的问题。
image-20240124102740174.png

我们来分步骤解释一下这个过程:

  • a 是一个包含两个元素的列表:[1, 2]
  • a[1] = a 将列表 a 的第二个元素设为 a,即a[1]实际上指向列表 a 本身,形成了一个循环引用
  • 当打印 a[1] 时,Python 发现这是一个特殊的情况,即这个元素是对列表本身的引用。为了避免无限循环,Python 会显示 ...,表示引用已经进入了一个循环。因此看到的结果是 [1, [...]]

那如何避免循环引用呢?可以使用浅拷贝或者深拷贝来解决。

我们用浅拷贝来试一下:

import copy

a = [1, 2]
a[1] = copy.copy(a)
print(a[1])

# 结果是[1,2]

浅拷贝创建一个新的对象,然后将原始对象中的元素复制到新对象中。但是,如果原始对象的元素是可变对象(例如列表),那么浅拷贝只会复制对象的引用而不是对象本身。

就比如上面的例子:

  • a = [1, 2] 创建了一个列表 a,其中有两个元素 1 和 2。
  • a[1] = copy.copy(a) 将列表 a 的第二个元素修改为对列表 a 的浅拷贝。
  • 打印 a[1],此时 a[1] 指向了新的对象 [1, 2]

关于浅拷贝深拷贝的内容咸鱼后期再写篇文章介绍一下!

相关文章
|
3天前
|
存储 Python 容器
[oeasy]python045_[词根溯源]赋值_assignment_usage_使用
本文回顾了上一次讲解的内容,重点讨论了变量的概念及其在各种系统和游戏中的应用。文章详细解释了变量的声明与赋值操作,强调了赋值即为将具体值存储到变量名下的过程。同时,通过例子说明了字面量(如数字0)不能被赋值给其他值的原因。此外,还探讨了“赋值”一词的来源及其英文表达“assignment”的含义,并简要介绍了与之相关的英语词汇,如sign、assign、signal等。最后,总结了本次课程的核心内容,即赋值操作的定义和实现方式。
10 3
|
25天前
|
人工智能 Python
[oeasy]python039_for循环_循环遍历_循环变量
本文回顾了上一次的内容,介绍了小写和大写字母的序号范围,并通过 `range` 函数生成了 `for` 循环。重点讲解了 `range(start, stop)` 的使用方法,解释了为什么不会输出 `stop` 值,并通过示例展示了如何遍历小写和大写字母的序号。最后总结了 `range` 函数的结构和 `for` 循环的使用技巧。
31 4
|
1月前
|
Python
【10月更文挑战第5天】「Mac上学Python 8」基础篇2 - 变量深入详解
本篇将详细介绍Python中变量的使用方式和进阶操作,涵盖变量的输入与输出、变量的多重赋值、变量的内存地址管理以及变量的传递和交换等操作。通过本篇的学习,用户将对变量的使用有更深入的理解,并能灵活运用变量进行各种编程操作。
52 1
【10月更文挑战第5天】「Mac上学Python 8」基础篇2 - 变量深入详解
|
1月前
|
存储 缓存 Java
深度解密 Python 虚拟机的执行环境:栈帧对象
深度解密 Python 虚拟机的执行环境:栈帧对象
62 13
|
1月前
|
存储 编译器 Python
Python--变量、输出与输入
【10月更文挑战第5天】
|
1月前
|
测试技术 Python
Python MagicMock: Mock 变量的强大工具
Python MagicMock: Mock 变量的强大工具
|
1月前
|
存储 Java 编译器
Python学习三:学习python的 变量命名规则,算数、比较、逻辑、赋值运算符,输入与输出。
这篇文章是关于Python编程语言中变量命名规则、基本数据类型、算数运算符、比较运算符、逻辑运算符、赋值运算符以及格式化输出与输入的详细教程。
20 0
Python学习三:学习python的 变量命名规则,算数、比较、逻辑、赋值运算符,输入与输出。
|
1月前
|
索引 Python
Python 对象的行为是怎么区分的?
Python 对象的行为是怎么区分的?
26 3
|
1月前
|
Python
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
19 1
|
1月前
|
缓存 Java 程序员
一个 Python 对象会在何时被销毁?
一个 Python 对象会在何时被销毁?
41 2
下一篇
无影云桌面