Python学习笔记十一动态类型


简介:在Python中,我们并不会声明脚本中使用的对象的确切类型。事实上,程序甚至可以不在意特定的类型;相反地,它们能够自然地适用于更广泛的场景下。因为动态类型是Python语言灵活性的根源,让我们先简要地看一下这个模块。

  1. 缺少类型声明语句的情况

    在Python中类型是运行过程中自动决定的,而不是通过代码声明。这意味着没有必要事先声明变量。这个概念实质上对变量,对象和他们之间的关系都适用。

  2. 变量,对象和引用
    1. 变量创建。变量a,当代码第一次给它赋值的时候就创建了a,之后的赋值会改变已创建的变量名的值。从技术上讲,Python在代码运行之前先检测变量名,可以当成是最初的赋值创建变量。
    2. 变量类型。变量永远不会有任何的和它相关的类型信息或约束。类型的概念是存在于对象中而不是变量中的,变量原本是通用的,它只是在一个特定的时间点,简单地引用了一个特定的对象而已。
    3. 变量使用。当变量出现在表达式中,它会马上被当前引用的对象所代替,无论这个对象是什么类型。此外,所有变量必须在其使用前明确的赋值,使用未赋值的变量会产生错误。
    4. 总结。变量在赋值的时候才创建,它可以引用任何类型的对象,并且必须在引用之前赋值。不需要通过脚本声明所需要使用的名字。但是,必须初始化名字然后才能更新它们。
    5. 对象名和变量。在运行a=3后,变量a变成对象3的一个引用。在内部,变量事实上是创建到对象内存空间(通过运行常量表达式3而创建)的一个指 针。在Python中从变量到对象的连接成为引用。也就是说,引用是一种关系,以内存中的指针的形式实现。一旦变量被使用也就是说被引用,Python自 动跟随这个变量到对象的连接。具体术语来讲: ->变量是一个系统表的元素,拥有指向对象的连接的空间。->对象是分配的一块内存,有足够的空间去表示他们所代表的值。->引用是自 动形成的从变量到对象的指针。在脚本中,每一次通过运行一个表达式生成一个新的值,Python都创建了一个新的对象即一块内存去表示这个值。从内部来 看,左右一种优化,Python会缓存了不变的对象并对其复用,例如,小的整数和字符串。但是,从逻辑的角度看,这工作起来就像每一个表达式结果的值都是 一个不同的对象,而每一个对象都是不同的内存。从技术上来叫,对象有更复杂的结构而不是仅仅是足够的空间表示它的值那么简单。每一个对象都有两个标准的头 部信息,一个类型标志符去标识这个对象的类型,以及一个引用的计数器,用来决定是不是可以回收这个对象。
  3. 类型属于对象,而不是变量

    变量名没有类型,类型属于对象,而不是变量名。我们给a赋不同的值,只是让变量引用了不同类型的对象而已。实际上,Python的变量就是特定的时候引用了一个特定的对象。另一方面,对象知道自己的类型。每个对象都包含了一个头部信息,标记了这个对象的类型。

  4. 对象的垃圾收集

    在Python中,每当一个变量名被赋予了一个新的对象,之前的那个对象占用的空间就会被回收(如果他没有被其它的变量名或对象引用的话)。这种自 动回收对象空间的技术叫做垃圾收集。在内部,Python是这样实现的:它在每个对象中保持了一个计数器,计数器记录了当前指向该对象的引用的数目,一旦 并精确到同一时间,这个计数器被设置为零,这个对象的内存空间就会被自动回收。

  5. 共享引用
    #ipython3
    
    In [1]: a = 3
    
    In [2]: b = a
    

    In2会使Python创建变量b,变量a正在使用,并且它在这里没有复制,所以它被替换成其引用的对象3,从而b也成为这个对象的一个引用。实际 效果就是变量a和b都引用了相同的对象,也就是指向了相同的内存空间。这在Python中叫做共享引用,即多个变量名引用了同一个对象。

    共享引用和在原处修改,注意观察以下几种情况

    	
    In [1]: a = 3
    
    In [2]: b = a
    
    In [3]: a = 4
    
    In [4]: a
    Out[4]: 4
    
    In [5]: b
    Out[5]: 3
    
    //a的改变不会引起b的改变
    
    
    In [1]: L1 = [2,3,4]
    
    In [2]: L2 = L1
    
    In [3]: L1[0]=24
    
    In [4]: L1
    Out[4]: [24, 3, 4]
    
    In [5]: L2
    Out[5]: [24, 3, 4]
    
    //L1中的某个值得变化影响了L2的对应值的变化
    
    
    In [1]: L1 = [2,3,4]
    
    In [2]: L2 = L1[:]
    
    In [3]: L1[0] = 24
    
    In [4]: L1
    Out[4]: [24, 3, 4]
    
    In [5]: L2
    Out[5]: [2, 3, 4]
    

    最后的代码块,修改L1中的值并不会改变L2对应的值,因为L2引用的是L1所引用对象的一个拷贝,也就是说,两个变量指向了不同的内存区域。

    注意这种分片技术不会应用到其它的可变的核心类型,比如字典和集合,因为它们不是序列。复制一个字典或集合应该 使用X.copy()方法调用。而且,注意标准库中copy模块有一个通用的复制任意对象类型的调用,有一个拷贝嵌套对象结果的调用,当学习到列表和字典 的时候,详细说明。

  6. 共享引用和相等

    Python缓存并复用了小的整数和小的字符串,但是大多数种类的对象都会在不再引用时马上回收,对于那些不会被回收的,缓存机制与代码并没有什么关系。例如,由于Python的引用模型,在Python程序中有两种不同的方法去检查是否相等

    In [1]: l = [1,2,3]
    
    In [2]: m = l
    
    In [3]: l == m
    Out[3]: True
    
    In [4]: l is m
    Out[4]: True
    

    实际上,is是比较实现引用的指针,是代码中检测共享引用的一种办法,如下

    In [1]: L = [1,2,3]
    
    In [2]: M = [1,2,3]
    
    In [3]: L == M
    Out[3]: True
    
    In [4]: L is M
    Out[4]: False
    
    In [5]: M is L
    Out[5]: False
    

    但是我们看看对小数的数字或者字符串操作的结果

    In [1]: X = 42
    
    In [2]: Y = 42
    
    In [3]: X == Y
    Out[3]: True
    
    In [4]: X is Y
    Out[4]: True
    
    In [5]: M = 'spam' 
    
    In [6]: N = 'spam' 
    
    In [7]: M == N
    Out[7]: True
    
    In [8]: M is N
    Out[8]: True
    
    //对象被缓存和复用的结果
    

    实际上,如果你想查询一个对象引用的次数,在sys模块中,getrefcount函数会帮你返回对象的引用次数。这种对象缓存和复用的机制与代码 是没有关系的(除非你运行这个检查)。因为不能改变数字和字符串,所以无论对同一个对象有多少个引用都没有关系。这种现象也反应了Python为了执行速 度而采用的优化其模块的众多方法中的一种

分类: Python3

0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注