为什么我们需要Python中的不可变对象?
当一个新手进入编程世界并开始学习它的不同概念并最终到达数据结构和算法,学习和实现它们时,他/她倾向于以一种或另一种方式阅读一次并忘记不可变对象。
可变和不可变存在于每一种编程语言中,但是人们只倾向于关心可变的,比如列表、队列等,而没有或很少注意不可变,因为乍一看它似乎只是一个附加组件而不是'似乎没有任何现实生活中解决问题的应用程序,为了揭穿这个想法,让我们讨论一下它的概念、需求和局限性。
什么是不可变对象?
可变性是Python中数据类型的一个区别属性,它与其他数据类型形成了很大的对比,它往往是数据类型在创建后允许对其进行修改的能力,可以向其中添加值以及可以从中弹出。另一方面,从另一方面来看,也有一些对象不遵循这个原则,并且是不可更改的,并且在定义之后不允许对其进行任何修改。它的状态无论如何都不能改变,一旦初始化,它往往代表一个常数值。示例 – 整数、复数、字符串、浮点数、元组、复数、冻结集。
因此,如果任何变量初始化了与这些不可变数据类型中的任何一种相对应的值,则永远无法更改它。要更改它,必须将相同的变量初始化为想要的修改。当一个变量被重新分配给其他字符串时,它往往会为两个对象分配不同的内存位置。
例子:
Python3
# string initialized
var = 'Geeks'
print(id(var))
print(var)
# Reassigned to another value
var = 'For Geeks'
# New Memory Location assigned
print(id(var))
print(var)
Python3
var1 = 'GFG'
var2 = 'GFG'
var3 = 'GFG'
var4 = 'GFG'
var5 = 'GFG'
# All the variables points to a
# single String
print(id(var1), id(var2), id(var3),
id(var4), id(var5))
输出:
139758810541392
Geeks
139758782345520
For Geeks
注意:在Python中,元组的不可变性往往是一个例外,因为元组本身是不可变的,但在初始化后无法修改,并且在初始化时给出的值是它持有的最终值,没有什么可以添加的或从中删除值,但是,像List这样嵌入在元组中的可变字段可以被修改而没有任何错误,但这证明了元组引用的对象可以被修改,这种现象有时被称为“不可传递不变性” 。
在Python中,编码人员在优化方面更有优势,主要针对字符串对象的不可变性。
例子:
Python3
var1 = 'GFG'
var2 = 'GFG'
var3 = 'GFG'
var4 = 'GFG'
var5 = 'GFG'
# All the variables points to a
# single String
print(id(var1), id(var2), id(var3),
id(var4), id(var5))
输出:
140080377558384 140080377558384 140080377558384 140080377558384 140080377558384
如果一个人倾向于一个接一个地创建 20 个字符串对象,保存相同的值,那么Python不会为每个值分配不同的内存位置,但它会让每个标识符引用相同的字符串,因为它永远不会进一步修改,从而节省大量内存。唯一的例外是相同的场景不适用于除字符串之外的每个不可变对象,但这种优化技巧被证明是依赖于实现的。
为什么要关心 Immutables ?
- 提高了整个代码的准确性和简单性,因为它使编码人员可以轻松地在程序中传递对象,而不必担心它似乎从未被修改过。而可变的更难推理。在 mutable 中,混叠会导致许多不规则性并最终威胁代码的保真度,最终导致可变性。
- 线程安全——由于分配后的对象不能被修改,这推断出线程之间正在共享只读数据,这当然提供了线程安全性。简而言之,在不可变对象中,由于对象没有变化范围,因此无需害怕从多个线程访问它。不可变对象在任何意义上的变异都可以通过创建一个新对象而不是试图修改现有对象的方式来实现。
限制
随着时间的推移,它引入了不同的结论,因为我们读到 Immutables 是线程安全的,并且无论哪个线程读取它们的值,它们都会得到正确的值,但是发现 immutables 不受“内存一致性错误”的影响,即可以进一步解释为;不可变对象本身不是线程安全的。使用它们的代码必须编写为线程安全的。仅仅使用不可变对象是不够的。还必须防止死锁、活锁和饥饿。