相关文章推荐
无聊的松球  ·  Amazon.com.br·  2 周前    · 
胆小的小蝌蚪  ·  滨州医学院·  7 月前    · 

Python中的值类型与引用类型

其实各个标准资料中没有说明Python有值类型和引用类型的分类,这个分类一般是C++和Java中的。但是语言是相通的,所以Python肯定也有类似的。实际上Python 的变量是没有类型的,这与以往看到的大部分语言都不一样(JS等弱类型的也是这样)。但 Python 却是区分类型的,那类型在哪里呢?事实是,类型是跟着内存中的对象走的。 类型 属于 对象 变量 是没有类型的。一般也分实参和形参。

《learning python》中的一个观点: 变量无类型,对象有类型。

不可变(immutable)对象类型

  • int
  • float
  • decimal
  • complex
  • bool
  • str
  • tuple
  • range
  • frozenset
  • bytes

可变(mutable)对象类型

  • list
  • dict
  • set
  • bytearray
  • user-defined classes (unless specifically made immutable)

Python中的变量都是指针,这确实和之前学过的强类型语言是有不同的。 因为变量是指针,所以所有的变量无类型限制,可以指向任意对象。指针的内存空间大小是与类型无关的,其内存空间只是保存了所指向数据的内存地址。

Python 的所有变量其实都是指向内存中的对象的一个指针,所有的变量都是!此外,对象还分两类:一类是可修改的,一类是不可修改的。我的理解是把 可修改(mutable)的类型叫做值类型,不可修改(immutable)类型叫做引用类型。

对象=确定内存空间+存储在这块内存空间中的值。

Java中,对象是分配在堆上的,存储真正的数据,而引用是在栈中开辟的内存空间用于引用某一个对象(值类型的变量也是存储到栈上)。

在Python中, 数值(整型,浮点型),布尔型,字符串,元组 属于 值类型 ,本身不允许被修改( 不可变类型 ),数值的修改实际上是让变量指向了一个新的对象(新创建的对象),所以不会发生共享内存问题。 这种方式同Java的不可变对象(String)实现方式相同。原始对象被Python的GC回收。
例子:

a = 1
b = a
a = 2
print(b)  #输出的结果是1

修改值类型的值,只是让它指向一个新的内存地址,并不会改变变量b的值。

>>> x = 1
>>> id(x)
31106520
>>> y = 1
>>> id(y)
31106520
>>> x = 2
>>> id(x)
31106508
>>> y = 2
>>> id(y)
31106508
>>> z = y
>>> id(z)
31106508
>>> x += 2
>>> id(x)
31106484 

类似于Java的字符串常量池。

  1. Python在底层做了一定的优化,对于使用过小整数以及短字符串都会被缓存起来。所以上述b引用的应该是被缓存过的3
  2. 之所以采用这种优化的方式,是因为python中数字和字符串一经创建都是不可修改的。所以不会出现,因使用了缓存的对象值造成“脏读”的问题

       对不可变数据类型中的int类型的操作,id()查看的是当前变量的地址值。我们先来看x = 1和y = 1两个操作的结果,从上面的输出可以看到x和y在此时的地址值是一样的,也就是说x和y其实是引用了同一个对象,即1,也就是说内存中对于1只占用了一个地址,而不管有多少个引用指向了它,都只有一个地址值,只是有一个引用计数会记录指向这个地址的引用到底有几个而已。当我们进行x = 2赋值时,发现x的地址值变了,虽然还是x这个引用,但是其地址值却变化了,后面的y = 2以及z = y,使得x、y和z都引用了同一个对象,即2,所以地址值都是一样的。当x和y都被赋值2后,1这个对象已经没有引用指向它了,所以1这个对象占用的内存,即31106520地址要被“垃圾回收”,即1这个对象在内存中已经不存在了。最后,x进行了加2的操作,所以创建了新的对象4,x引用了这个新的对象,而不再引用2这个对象。

       那么为什么称之为不可变数据类型呢?这里的不可变大家可以理解为x引用的地址处的值是不能被改变的,也就是31106520地址处的值在没被垃圾回收之前一直都是1,不能改变,如果要把x赋值为2,那么只能将x引用的地址从31106520变为31106508,相当于x = 2这个赋值又创建了一个对象,即2这个对象,然后x、y、z都引用了这个对象,所以int这个数据类型是不可变的,如果想对int类型的变量再次赋值,在内存中相当于又创建了一个新的对象,而不再是之前的对象。从下图中就可以看到上面程序的过程。

备注:图片中错误。x=2,y=2,z=y。 

从上面的过程可以看出,不可变数据类型的优点就是内存中不管有多少个引用,相同的对象只占用了一块内存,但是它的缺点就是当需要对变量进行运算从而改变变量引用的对象的值时,由于是不可变的数据类型,所以必须创建新的对象,这样就会使得一次次的改变创建了一个个新的对象,不过不再使用的内存会被垃圾回收器回收。

在Python中,列表,集合,字典引用类型,本身允许修改(可变类型)。

list_a = [1,2]
list_b = list_a
list_a[0] = 3
print(list_b)  #此时的输出结果是[3,2]


修改引用类型的值,因为list_b的地址和list_a的一致,所以也会被修改

一般只为了复制值,可以使用分片操作。还可以使用拷贝操作。

list_b = list_a[:]

>>> a = [1, 2, 3]
>>> id(a)
41568816
>>> a = [1, 2, 3]
>>> id(a)
41575088
>>> a.append(4)
>>> id(a)
41575088
>>> a += [2]
>>> id(a)
41575088
[1, 2, 3, 4, 2]

  从上面的程序中可以看出,进行两次a = [1, 2, 3]操作,两次a引用的地址值是不同的,也就是说其实创建了两个不同的对象,这一点明显不同于不可变数据类型,所以对于可变数据类型来说,具有同样值的对象是不同的对象,即在内存中保存了多个同样值的对象,地址值不同。接着来看后面的操作,我们对列表进行添加操作,分别a.append(4)和a += [2],发现这两个操作使得a引用的对象值变成了上面的最终结果,但是a引用的地址依旧是41575088,也就是说对a进行的操作不会改变a引用的地址值,只是在地址后面又扩充了新的地址,改变了地址里面存放的值,所以可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。

可变数据类型是允许同一对象的内容,即值可以变化,但是地址是不会变化的。但是需要注意一点,对可变数据类型的操作不能是直接进行新的赋值操作,比如说a = [1, 2, 3, 4, 5, 6, 7],这样的操作就不是改变值了,而是新建了一个新的对象,这里的可变只是对于类似于append、+=等这种操作。

不可变的例外

并非所有的不可变对象都是不可变的。

如前所述,Python容器比如元组,是不可变的。这意味着一个tuple的值在创建后无法更改。但是元组的“值”实际上是一系列名称,它们与对象的绑定是不可改变的。关键点是要注意绑定是不可改变的,而不是它们绑定的对象。

让我们考虑一个元组t =('holberton',[1,2,3])

上面的元组t包含不同数据类型的元素,第一个元素是一个不可变的字符串,第二个元素是一个可变列表。元组本身不可变。即它没有任何改变其内容的方法。同样,字符串是不可变的,因为字符串没有任何可变方法。但是列表对象确实有可变方法,所以可以改变它。这是一个微妙的点,但是非常重要:不可变对象的“值” 不能改变,但它的组成对象是能做到改变的

其实主要原因是元组内保存的是变量(也就是内存地址)。所以当变量指向对象发生变化时,如果导致变量发生变化(即不可变类型),此时元组保证该不可变类型不能修改。而如果当变量指向对象发生变化时,如果不会导致变量发生变化(即可变类型),此时元组中存储的该可变类型可以修改(因为变量本身并无变化)。

可变类型的赋值、拷贝,参数传递和效率问题请看本人其他博客。

python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象;可变数据类型,允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。

C++中是传值和传引用(指针)。c语言加上*号传递指针就是引用传递,而直接传递变量名就是值传递)

Java是传值(传值和传引用,只不过引用就是内存地址,所以也是值)。Java里区分值和引用,是因为值存储在栈里,而引用对象存储在堆里(引用本身在栈里)。

而Python所有的都是对象,都是引用,所以所谓的值类型都是不可变类型。类似于Java的字符串类型。

所以Python中的参数传递都是传递引用,也就是传递的是内存地址。只不过对于不可变类型,传递引用和传递值没什么区别。而对于可变类型,传递引用是真的传递内存的地址。

听说python只允许引用传递是为方便内存管理,因为python使用的内存回收机制是计数器回收,就是每块内存上有一个计数器,表示当前有多少个对象指向该内存。每当一个变量不再使用时,就让该计数器-1,有新对象指向该内存时就让计数器+1,当计时器为0时,就可以收回这块内存了。当然还有其他的GC方法,否则计数器回收,无法解决循环引用的问题。

不可变类型对效率和性能影响很大的,具体参照其他博客。

值传递:表示直接传递变量的值,把传递过来的变量的值复制到形参中,这样在函数内部的操作不会影响到外部的变量。

引用传递:把引用理解为变量(标识符)与数据之间的引用关系,标识符通过引用指向某块内存地址。而引用传递,传递过来的就是这个关系,当你修改内容的时候,就是修改这个标识符所指向的内存地址中的内容,因为外部也是指向这个内存中的内容的,所以,在函数内部修改就会影响函数外部的内容。

另外:列表,字典,集合是容器类型,嵌套引用。

Python 中一切都是对象,所有Python对象都拥有三个特性:身份、类型、值。 身份:每个对象都有一个唯一的身份标识,任何对象的身份可以使用内建函数id()来得到 类型:对象的类型决定了该对象可以保存 Python基础数据类型Python3 中有六个标准的数据类型:Number(数字)String(字符串)List(列表)Tuple(元组)Set(集合)Dictionary(字典)不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。String(字符串)字符串简述str.__d... 作为一个python初学者,今天被一个python列表和词典引用的问题折磨了很久,但其实了解了缘由也很简单,记录在此备忘。首先背书python中的引用对象问题:1. python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值——相当于... 问题导入? 如果你使⽤Python声明了⼀个变量并同时为其赋值,即a = 20,你是否将这条语句简单理解为:1. 计算机开辟了⼀块地址别名为a的内存;2. 往这块内存存储了数据20? 如果你对上述问题的回答为是,那么你对诸如“Python是⼀门动态类型语⾔”、“Python效率远不如C语⾔等静态语⾔效率⾼”等说法也⼀定不了. 其实各个标准资料中没有说明Python有值类型引用类型的分类,这个分类一般是C++和Java中的。但是语言是相通的,所以Python肯定也有类似的。实际上Python 的变量是没有类型的,这与以往看到的大部分语言都不一样(JS等弱类型的也是这样)。但 Python 却是区分类型的,那类型在哪里呢?事实是,类型是跟着内存中的对象走的。类型属于对象,变量是没有类型的。一般也分实参和形参。《learn... 在Python程序中,每个数据都是对像,每个对像都有自己的一个类型。不同类型有不同的操作方法,使用内置数据类型独有的操作方法,可以更快的完成很多工作。python中一些基本数据类型,比如:整型(数字)、字符串、元组、列表、字典和布尔类型。随着学习进度的加深,大家还会接触到更多更有趣的数据类型python初学者入门时先了解这几种类型就可以了。python基本内置数据类型对应符号1.整型——int—... 我正在用Python2.6.2编写一个包含查找表的类。大多数情况都很简单,表中包含数据。有些情况比较复杂,我希望能够调用函数。但是,我在引用函数时遇到了一些问题。下面是一些示例代码:class a:lut = [1,3,17,[12,34],5]其中lut是静态的,并且预期也是常量。现在我要做的是:class a:def spam0(self):return (some_calculation_b... 如果是在同一个 module中(也就是同一个py文件里),直接用就可以 如果在不同的module里,例如 a.py里有 class A: b.py 里有 class B: 如果你要在class B里用class A 需要在 b.py的开头写上 from a import A。如果你要在class B里用class A 需要在 b.py的开头写上 from a import A。类名要和文件名符合,且引用时要注意原始文件类的基类与派生类的关系。如果在不同的module里,例如。 1.2当变量对应的数据类型为列表类型时 由1和2我们可以看到,对字符串类型的修改后,访问原赋值对象得到的结果不变;而对列表类型修改后,访问原赋值对象得到修改后的值。这两者有着本质的区别。 2.python中的数据类型都是保存在内存中,python中的数据分为两大类: 不可变数据类型:数字,布尔,元组,字符串 可变数... 其实各个标准资料中没有说明Python有值类型引用类型的分类,这个分类一般是C++和Java中的。但是语言是相通的,所以Python肯定也有类似的。实际上Python 的变量是没有类型的,这与以往看到的大部分语言都不一样(JS等弱类型的也是这样)。但 Python 却是区分类型的,那类型在哪里呢?事实是,类型是跟着内存中的对象走的。类型属于对象,变量是没有类型的。一般也分实参和形参。《learn... 一、ptyhon 介绍ptyhon语言是指Python的语言风格,和它自己的语法结构等,python解释器是去解释并执行的一个软件,Cpython(最常用的版本)Python的官方版本,使用C语言实现,使用最为广泛,CPython实现会将源文件(py文件)转换成字节码文件(pyc文件),然后运行在Python虚拟机上。JyhtonPython的Java实现,Jython会将Python代码动态编译... 可变对象和不可变对象 python变量保存的是对象的引用,这个引用指向堆内存里的对象,在堆中分配的对象分为两类,一类是可变对象,一类是不可变对象。不可变对象的内容不可改变,保证了数据的不可修改(安全,防止出错),同时可以使得在多线程读取的时候不需要加锁。 不可变对象(变量指向的内存的中的值不能够被改变) 当更改该对象时,由于所指向的内存中的值不可改变,所以会把原来的值复制到新的空