Python中的可变对象与不可变对象

从整数到字符串再到函数。 Python中的所有内容都是一个对象。 由Guido van Rossum设计的Python是一种高级OOP语言,其开发重点是简单性和代码可读性。 在此博客中,我们将重点研究Python中的各种对象。

ID和类型

对象的ID是唯一的内存地址,该地址在创建对象时分配。 内置函数“ id”可用于确定对象的ID。 乍一看,id函数可能没有提供太多实用程序,但是它由python’is’使用 运算符确定两个变量引用同一对象。

  >>> a = [1、2、3] 
>>> b = [1、2、3]
>>> a是b

>>> id(a)
4329978760
>>> id(b)
4329945800

在上面的示例中,我们创建了两个具有相同值的列表对象。 但是,它们各自存在于不同的存储器地址。 为什么是这样? 列表是可变对象。 在下一节中,我们将讨论更多有关对象的可变性如何影响变量行为的内容。

在继续之前,让我们进一步了解Python中的类型。 对象的类型是该对象所属的类。 内置的“类型”功能有助于确定对象的类型,而无需支持继承和忽略超类。

  >>> a = [1、2、3] 
>>>类型(a)

可变对象

如果可以在创建对象后修改其值,则该对象被称为可变对象。 列表,集合,字典是可变对象,因为您可以添加元素或从中删除元素。 在上面的示例中,我们发现具有相同元素的两个列表具有不同的内存地址。 即使变量引用具有相同元素的对象,每个变量也都绑定到不同的对象。 这意味着更改一个对象不会更改另一个对象。

  >>> a = [1、2、3] 
>>> b = [1、2、3]
>>> a.append(4)
>>>一个
[1、2、3、4]
>>> b
[1,2,3]

如果要变量绑定到同一对象,则需要为该对象创建别名。 因此,使用原始变量或别名变量修改对象将导致另一个对象显示相同的更改

  >>> a = [1、2、3] 
>>> b = a
>>> b.append(4)
>>> b
[1、2、3、4]
>>>一个
[1、2、3、4]

这对许多人来说似乎很明显,但是不可变对象的行为却有所不同,我们将在下一部分中进行研究。

不变的对象

不可变对象是其值在创建后无法修改的对象。 不变对象的几个例子是整数,字符串和元组。

  >>> a =(1、2、3)#一个元组 
>>> a + =(4,)#将4加到元组
>>>一个
(1,2,3,4)

等待。 在上面的示例中,我们只是创建和修改了一个元组,即使它是“不可变的”吗? 假。 如果我们仔细研究,Python足够聪明,可以理解严格禁止修改元组,因此它继续为我们创建了一个新对象并将其分配给变量“ a”。

  >>> id(a) 
4329915000
>>>一个+ =(5,)
>>>一个
(1,2,3,4,5)
>>> id(a)
4302333824

我们可以看到,如果在添加新元素之前和之后检查变量“ a”的内存地址,这确实是正确的。 现在,我们的变量“ a”引用了完全不同的内存地址。 不可变对象的另一个简洁属性是具有相同值的对象共享相同的内存地址。 回到带有两个列表的第一个示例,尽管元素相同,但它们是不同的对象。 让我们讨论以下示例:

  >>> a =“ foo” 
>>> b =“ foo”
>>> a是b
真正

当两个不可变对象的值相同时,Python会通过将两个引用对象的变量设置为同一对象来优化空间。 我们将两个变量都初始化为都等于“ foo”,但是没有收到与列表相同的行为。 那是因为字符串是不可变的。 不可变的对象无法更改,因此缓存一个对象并将其保留在内存中是有意义的,并且可以安全地允许其他变量引用该对象,而不必担心如果对某个变量进行更改会影响另一个变量。

但是,此属性不会扩展到所有不可变对象。 我们将讨论不可变和易变的对象的某些陷阱,以及为什么它们对于理解作为Python开发人员很重要。

为什么这一切都很重要

继续我们对不可变对象的讨论,我想指出的是,即使其值可能相同,但并非所有不可变对象都共享相同的内存地址。

  >>> a = 1024 
>>> b = 1024
>>> a是b

Python是偷偷摸摸的。 运行Python程序后,该语言会在内存中缓存一组常用对象。 在Python甚至没有运行第一行代码之前,就创建了262个整数对象。 整数-5(含)到256(含)被缓存在堆中,因为创建多个常用整数(如-1、0或0)的副本很浪费。创建任何变量来引用这262个整数对象,并带有相同的值将具有相同的ID。

  >>> a = 1 
>>> b = 1
>>> a是b
真正

但是,一旦超过了这个数字范围,Python就会为您创建一个新的Integer对象,并且引用该对象的任何变量将具有相同的值,并且具有不同的内存地址,如前面的示例之一所示。

元组的另一个陷阱。 空元组共享相同的地址,但其他任何元组将具有不同的地址。 另外,即使不能更改元组,也可以更改元组内部的元素。

  >>> a =(1、2,[“ foo”]) 
>>> a [2] [0] =“ bar”
>>>一个
(1、2 [[bar]])

不正确使用不可变变量不仅危险,而且可变变量也需要谨慎。

默认参数陷阱

  def示例(列表= []): 
list.append(“ first”)
打印(列表)

Python中的一种常见做法是使用默认参数。 当将参数传递给函数时,python创建新的局部变量,并且传入的对象被变量引用。 对这些对象所做的任何更改都会反映回传递给函数的原始变量; 除非它是一个不变的对象。

如果没有参数传递给函数示例,则该函数将使用默认参数:空列表。 每次调用此函数时,该函数都会在列表中附加字符串“ first”。 如果我们多次调用此函数并使其打印列表的内容,我们将认识到使用可变对象作为默认参数的缺陷。

  >>> example() 
['第一']
>>> example()
[“第一”,“第一”]
>>> example()
[“第一”,“第一”,“第一”]

噢亲爱的。 似乎我们每次调用函数都修改了列表。 函数的默认参数在函数创建期间初始化一次。 如果要更改变量列表,则函数的其他每一次迭代都将反映这些更改。 这就是为什么除非您是专家或知道自己在做什么,否则使用可变默认参数很危险。

希望您在学习Python中的不变和可变对象时玩得开心。

就是这样,伙计们。

参考文献:

https://images.idgesg.net/images/article/2017/07/python_snake_programming-language-100728352-large.jpg

陷阱-可变的默认参数

可变和不可变对象| 曼宁

在“获取编程”上节省37%。 只需在manning.com的结帐处的折扣代码框中输入代码fccbell。 不变的……

freecontent.manning.com