【Python 学习手册笔记】动态类型简介

来源:互联网 发布:立体设计图软件 编辑:程序博客网 时间:2024/05/18 02:34


第6章
动态类型简介

动态类型以及由它提供的多态性,这些概念无疑是Python语言简洁性和灵活性的基础。
在Python 中,我们并不会声明脚本中使用的对象的确切类型。事实上,程序甚至可以不在意特定的类型,相反地,它们能够自然地适用于更广泛的场景下.


缺少类型声明语句的情况
在交互会话模式或是程序文件中,当输入a = 3时. Python怎么知道那代表一个整数呢? 在这种情况下. Python怎么知道a是什么?
在Python中,类型是在运行过程中自动决定的,而不是通过代码声明。这意味着没有必要事先声明变量;


变量、对象和引用
当在Python 中运行赋值语句a = 3时,即使没有告诉Python将a作为一个变量来使用,或者没有告诉它a应该作为一个整数类型对象,但一样也能工作。在Python语言中,这些都会以一种非常自然的方式完成,就像下边这样:

       变量创建
       一个变量(也就是变量名),就像a,当代码第一次给它赋值时就创建了它。之后的赋值将会改变已创建的变量名的        值。从技术上来讲,Python在代码运行之前先检测变量名,可以当成是最初的赋值创建变量。
  
       变量类型
       变量永远不会有任何的和它关联的类型信息或约束。类型的概念是存在于对象中而不是变量名中。变量原本是通

       用的,它只是在一个特定的时间点,简单地引用了一个特定的对象而已。
  
       变量类型
       变量永远不会有任何的和它关联的类型信息或约束。类型的概念是存在于对象中而不是变量名中。变量原本是通

       用的,它只是在一个特定的时间点,简单地引用了一个特定的对象而已。
  
总而言之,变量在赋值的时候才创建,它可以引用任何类型的对象,并且必须在引用之前赋值。
如果清楚地将变量名和对象划分开来,动态类型是很容易理解的。
例如,当我们这样说时:
>>> a = 3
至少从概念上来说, Python将会执行三个不同的步骤去完成这个请求。这些步骤反映了Python 语言中所有赋值的操作:
  1.创建一个对象来代表值3。
  2.创建一个变量a,如果它还没有创建的话。
  3.将变量与新的对象3相连接。
 
变量总是连接到对象,并且绝不会连接到其他变量上,但是更大的对象可能连接到其他的对象(例如,一个列表对象能够连接到它所包含的对象)。在内部, 变量事实上是到对象内存空间(通过运行常量表达式3而创建)的一个指针;

在Python 中从变量到对象的连接称作引用。也就是说,引用是一种关系,以内存中的指针的形式实现,一旦变量被使用(也就是说被引用) , Python 自动跟随这个变量到对象的连接。以具体的术语来讲:
  1.变量是一个系统表的元素,拥有指向对象的连接的空间。
  2.对象是分配的一块内存, 有足够的空间去表示它们所代表的值。
  3.引用是自动形成的从变量到对象的指针。
  
至少从概念上讲,在脚本中,每一次通过运行一个表达式生成一个新的值,Python都创建了一个新的对象(换言之,一块内存)去表示这个值。从内部来看,作为一种优化,Python缓存了不变的对象并对其进行复用,例如,小的整数和字符串(每一个0都不是一块真正的、新的内存块,稍后会介绍这种缓存行为)。但是, 从逻辑的角度看,这工作起来就像每一个表达式结果的值都是一个不同的对象,而每一个对象都是不同的内存。

从技术上来讲,对象有更复杂的结构而不仅仅是有足够的空间表示它的值那么简单。每一个对象都有两个标准的头部信息: 一个类型标志符去标识这个对象的类型,以及一个引用的计数器,用来决定是不是可以回收这个对象。

类型属于对象,而不是变量
在Python 中,变量名没有类型,就像前边所说的,类型属于对象,而不是变量名。Python的变量就是在特定的时间引用了一个特定的对象。从另一方面讲,对象知道自己的类型。每个对象都包含了一个头部信息,其中标记了这个对象的类型。

对象的垃圾收集
在Python中,每当一个变量名被赋予了一个新的对象,之前的那个对象占用的空间就会被回收(如果它没有被其他的变量名或对象所引用的话)。这种自动回收对象空间的技术叫做垃圾收集。

在内部,Python是这样来实现这一功能的: 它在每个对象中保持了一个计数器,计数器记录了当前指向该对象的引用的数目。一旦(并精确在同一时间)这个计数器被设置为零,这个对象的内存空间就会自动回收。
要了解Python的循环检测器的更多细节,参见Python库手册中gc模块的文挡。


共享引用
在Python中,多个变量名引用了同一个对象叫做共享引用。
对一个变量赋值,仅仅会影响那个被赋值的变量。

共享引用和在原处修改
有一些对象和操作确实会在原处改变对象。例如,在一个列表中对一个偏移进行赋值确实会改变这个列表对象,而不是生成一个新的列表对象。对于支持这种在原处修改的对象,共享引用时的确需要加倍的小心,因为对一个变量名的修改会影响其他的变量。
>>> L1 = [2, 3, 4]
>>> L2 = L1
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[24, 3, 4]
我们实际上并没有改变L2 ,但是它的值将发生变化,因为它已经被修改了。
如果你不想要这样的现象发生,需要Python拷贝对象,而不是创建引用。有很多种拷贝一个列表的办法,包括内置列表函数以及标准库的copy模块。也许最常用的办法就是从头到尾的分片。
>>> L1 = [2, 3, 4]
>>> L2 = L1[:]
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[2, 3, 4]

这种分片技术不会应用在其他的可变的核心类型(字典和集合,因为它们不是序列)上,复制一个字典或集合应该使用x.copy()方法调用。而且,注意标准库中的copy模块有一个通用的复制任意对象类型的调用,也有一个拷贝嵌套对象结构(例如,嵌套了列表的一个字典)的调用:
>>> import copy
>>> X = copy.copy(L1)          # Make top-level "shallow" copy of any objecl Y
>>> X = copy.deepcopy(L1)  # Make deep copy of ally objecl Y: copy all nested parts


共享引用和相等
出于完整的考虑,本章前面介绍的垃圾收集的行为与常量相比,某些类型需要更多地思考。参照下边的语句:
>>> x = 42
>>> x = 'shrubbery' # Reclaim 42 now?
因为Python缓存并复用了小的整数和小的字符串,就像前文提到的那样,这里的对象42也许并不像我们所说的被回收;相反地,它将可能仍被保存在一个系统表中, 等待下一次你的代码生成另一个42来重复利用。尽管这样,大多数种类的对象都会在不再引用时马上回收,对于那些不会被回收的,缓存机制与代码并没有什么关系。

由于Python的引用模型,在Python程序中有两种不同的方法去检查是否相等
>>> L = [1, 2, 3]
>>> M = L
>>> L == M
True
>>> L is M
True

这里的第一种技术"==操作符",测试两个被引用的对象是否有相同的值。这种方法往往在Python中用作相等的检查。第二种方法" is操作符",是在检查对象的同一性。如果两个变量名精确地指向同一个对象,它会返回True,所以这是一种更严格形式的相等测试。
>>> L = [1, 2, 3]
>>> M = [1, 2, 3]    #M and L reference different objects
>>> L == M            #Same values
True
>>> L is M              #Different objects
False


>>> X = 42
>>> Y = 42
>>> X == Y
True
>>> X is Y
True
因为小的整数和字符串被缓存并复用了,所以is告诉我们X和Y是引用了一个相同的对象。

你能够向Python查询对一个对象引用的次数: 在sys模块中的getrefcount 函数会返回对象的引用次数。

0 0