Python不可变对象与可变对象

简介: Python语言是一个以**一切皆对象**的面向对象的动态型语言。Python的对象可以根据其是否可以变化划分为可变对象和不可变对象。

Python语言是一个以一切皆对象的面向对象的动态型语言。Python的对象可以根据其是否可以变化划分为可变对象和不可变对象。

对象类型

不可变对象(值类型)

  • Numbers:数值类型
    • int: 整型数
    • float: 浮点数
    • complex: 复数
  • bool: 布尔类型
  • str: 字符型
  • tuple: 元组
  • range: 范围对象
  • frozenset: 不可变集合
  • bytes: 不可变字节数组

可变对象(引用类型)

  • list: 列表
  • dict: 字典
  • set: 可变集合
  • bytearray: 可变字节数组
  • 用户自定义类

在编码中,我们可以对如str, int等对象进行修改,那为什么这些类型还是不可变对象。

实际上,当我们对这些不可变对象进行操作时,我们更改的并不是变量指向对象具体的值,而是使变量重新指向了一个不可变对象。

a = 123456789
b = 123456789

print(f"id(a) = {id(a)}, id(b) = {id(b)}")

# id(a) = 140219002169648, id(b) = 140219002169648

如上我们可以看到,当Python变量指向相同不可变对象时,不同的变量实际上是引用的相同的对象。

a = 123456789
print(f"id(a) = {id(a)}")

a = 987654321
print(f"update_id(a) = {id(a)}")

# id(a) = 140504869153072
# update_id(a) = 140504869153008

如上我们可以看到,当Python修改不可变对象时,实际上只是改变的变量指向的对象,而不是原来的不可变对象。

不可变对象的特例

元组作为Python中的容器类型,其本身是不可变的,但容器中存储的对象不一定都是不可变对象。
考虑的一下这种情形:

a = (1, 2, 3, [1, 2, 3])
print(f"a = {a}")
print(f"id(a) = {id(a)}")
a[3].append(4)
print(f"update_a = {a}")
print(f"update_id(a) = {id(a)}")

# a = (1, 2, 3, [1, 2, 3])
# id(a) = 140188517471056
# update_a = (1, 2, 3, [1, 2, 3, 4])
# update_id(a) = 140188517471056

可以看到,元组明明是不可变对象,但我们却对其值进行了修改。主要原因是,元组作为容器,内部存储的都是其保存数据的内存地址,我们对该地址上的数据进行了操作修改,但没有改变其内存地址本身,所以元组的值自然而然的就发生了变化。
所以,不可变对象的“值”不可以改变,但其组成对象的“值”可以进行修改。在处理不可变对象时,要注意考虑到这种情形。

Python变量

Python中的变量都是指针,因为Python的变量都是指针,所以Python变量上无类型限制的,它是可以指向任意对象的,Python对象只是保存了指向数据的内存地址。

不可变对象(值类型)

在Python中,因为值类型作为不可变对象,他们本身的值是不可以修改的。所以对值类型的修改实际上是让变量指向了新的对象,原始对象会被Python的GC回收

a = 1
b = a
a = 2

print(b)
# 1

修改值类型的值,因为是让变量指向了新的对象,不会对原始对象的属性造成影响。

可变对象(引用类型)

在Python中,当修改引用类型即可变对象时,因为对象是可变的的,所以可以直接对引用的对象进行操作。因为引用对象是对该实例的内存空间上的值进行修改,所以当有多个变量引用同一个引用实例时,对一个变量的修改,其他引用该实例的变量也会发生相应的变化。

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

print(b)
# [1, 2, 3, 4]

可以看到通过对变量a进行操作,变量b的值也发生了相应的变化。

参数传递

函数按照传值的方式分为:

  • 值传递:把调用函数时传递的值赋值到形参当中,对形参的操作不会影响外部的实参变量。
  • 引用传递:把实参引用的内存地址赋值给形参,当对该内存的值进行修改时,会相应的影响到外部实参变量。

    参数传递方式还包括地址传递,在Python这种无指针的高级语言中,不考虑辨别区分地址传递这种类型

a = 123
b = [1, 2, 3]

print(f"id(a) = {id(a)}, id(b) = {id(b)}")


def test(a, b):
    print(f"func id(a) = {id(a)}, id(b) = {id(b)}")


test(a, b)
# id(a) = 140362719137840, id(b) = 140362724287744
# func id(a) = 140362719137840, id(b) = 140362724287744

通过上面的代码,可以看到Python可变对象和不可变对象在传递参数时,都是传递的变量指向的内存地址而不是进行的值传递。

Python为了方便内存的管理,都是采用的引用传递。在传递参数时,都传递的是对应的内存地址,所以在Python中对可变对象的修改,会引起外部对象的改变。不可变对象因为其特性,对不可变对象的操作效果和值传递具有相同的效果。

相关文章
|
6天前
|
网络协议 Python
Python网络编程基础(Socket编程)创建UDP socket对象
【4月更文挑战第8天】在Python中创建UDP服务器涉及使用`socket`模块创建socket对象,如`udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)`,然后绑定到特定IP地址和端口,如`udp_socket.bind(('localhost', 12345))`。服务器通过`recvfrom`在无限循环中监听和接收数据报。这只是基础,实际应用还需处理接收、解析、响应及错误处理等。接下来可学习如何利用socket对象进行数据交互以构建完整服务器。
|
6天前
|
存储 安全 UED
一文让你读懂Python中的Response对象
一文让你读懂Python中的Response对象
52 0
|
6天前
|
SQL API Python
Python DB API下规范下cursor对象常用接口
Python DB API下规范下cursor对象常用接口。
22 4
|
19小时前
|
搜索推荐 开发工具 Python
2024年最新【Python 基础教程】对时间日期对象的侃侃而谈,面试必考题
2024年最新【Python 基础教程】对时间日期对象的侃侃而谈,面试必考题
2024年最新【Python 基础教程】对时间日期对象的侃侃而谈,面试必考题
|
5天前
|
存储 Java 数据安全/隐私保护
Python----类对象和实例对象
Python----类对象和实例对象
9 2
|
6天前
|
存储 安全 Java
Python中的引用和赋值机制允许变量引用内存中的对象,并通过引用计数来管理对象的生命周期
【5月更文挑战第14天】Python中的变量是对象引用,不存储数据,而是在内存中创建对象。赋值操作创建新变量并使其指向已有对象。引用计数用于管理对象生命周期,引用数为0时对象被回收。理解这些机制对编写高效Python代码很重要。
18 6
|
6天前
|
C++ Python
Python中的类与对象
Python中的类与对象
8 1
|
6天前
|
缓存 Java Python
Python 弱引用全解析:深入探讨对象引用机制!
Python 弱引用全解析:深入探讨对象引用机制!
22 3
|
6天前
|
运维 Shell Sentinel
第八章 Python可迭代对象、迭代器和生成器
第八章 Python可迭代对象、迭代器和生成器
|
6天前
|
数据安全/隐私保护 Python
Python从入门到精通——2.2.1深入学习面向对象编程:类和对象的定义
Python从入门到精通——2.2.1深入学习面向对象编程:类和对象的定义