Appearance
前置基础-Python变量类型及动态特性【加推篇】
前置基础:Python变量类型及动态特性
前言
任何语言工具的变量都是以特定类型的形式存在的,Python中也不例外。
在Python中变量类型主要包括Number(数字)、String(字符串)、List(列表)、Tuple(元组)和Dictionary(字典)等,不过在Python中变量不需要预先声明类型就可以使用,这个即是所谓的动态类型特性。
关于这个特点很多初学者都会难以理解,那么本小节就来讲解下Python中变量的类型及动态特性。
变量类型的种类
刚才讲到Python中变量类型主要包括数字、字符串、列表、元组和字典等,那么此处对这些主要类型的使用做个简单的介绍,为后面动态特性的讲解做个基础铺垫。
数字类型用于存储数值,其中又包括了int(有符号整型)、long(长整型)、float(浮点型)、complex(复数)这几种,对于数字我们可以使用加(+)、减(-)、乘(*)、除(/)、取模(%)、幂(**)、取整除(//)等运算符进行运算。我们通过例程来了解下。
#例程代码:
#查看数值常量的类型 注:type()用于判断数据类型
print(type(123)) #结果为:<class 'int'>
print(type(1.12)) #结果为:<class 'float'>
print(type(3j + 1)) #结果为:<class 'complex'>
#查看数值常量的运算
print(1.0/3) #结果为:0.3333333333333333 注:Python3.x中为print(1/3)即可
print(12.5*10) #结果为:125.0
#查看数值变量的运算
a = 1
b = 3
print(a + b) #结果为:4
print(a - b) #结果为:-2
print(a * b) #结果为:3
print(a / b) #结果为:0.3333333333333333 注:Python2.x中除法的取值结果取整数 0
print(a % b) #结果为:1
print(a ** b) #结果为:1
print(a // b) #结果为:0
字符串类型是由数字、字母、下划线组成的一串字符,它是Python中表示文本的数据类型。我们通过例程来了解下。
#例程代码:
#查看字符串常量
print('hello world!') #结果为:hello world!
print("hello world!") #结果为:hello world!
#查看字符串变量
a = "hello world!"
print(a) #结果为:hello world!
列表是 Python 中使用最为频繁的数据类型,它可以实现大多数集合类的数据结构,支持字符,数字,字符串甚至可以嵌套列表。我们通过例程来了解下。
#例程代码:
#创建列表变量
list1 = ['physics', 'chemistry', 1997, 2000]
list2 = [1, 2, 3, 4, 5, 6, 7 ]
#列表访问
print(list1[2]) #结果为:1997
print(list2[1:5]) #结果为:[2, 3, 4, 5]
#列表二次赋值
list1[2] = 2001;
print(list1[2]) #结果为:2001
元组(tuple)用"()"标识,内部元素用逗号隔开,类似于列表(List),但是元组不能二次赋值,相当于只读列表。
#例程代码:
#创建元组并初始化
name_1 = ("Madonna", "Cory", ["Annie", "Nelly"], "Cory")
#元组访问
print(name_1) #结果为:('Madonna', 'Cory', ['Annie', 'Nelly'], 'Cory')
print(name_1[1:3]) #结果为:('Cory', ['Annie', 'Nelly'])
print(name_1.index("Madonna")) #结果为:0
print(len(name_1)) #结果为:4
字典(dictionary)用"{ }"标识,由索引(key)和它对应的值value组成。字典是除列表以外Python之中最灵活的内置数据结构类型。列表是有序的对象结合,字典是无序的对象集合,字典当中的元素是通过键来存取的,而不是通过偏移存取。
#例程代码:
#创建字典变量
math_score = {'Madonna': 89, 'Cory': 99, 'Annie': 65, 'Nelly': 89}
print(math_score) #结果为:{'Madonna': 89, 'Cory': 99, 'Annie': 65, 'Nelly': 89}
#字典访问
print(math_score['Madonna']) #结果为:89
动态类型的特性
本小节第一部分中我们在创建数值变量例程中执行了a = 1这行语句,学过像C语言这类的静态编译类型语言的同学,这里或许会有些困惑。在Python中输入a=1时,变量居然不需要预先声明类型,那Python怎么知道a为一个整数呢?这其实是Python作为动态类型语言的特点 。
在C语言中变量所分配到的地址是内存空间中一个固定的位置,当我们改变变量值时, 对应内存空间中的值也相应改变。在Python中变量存储的机制完全不一样。当给一个变量赋值时首先解释器会给这个值分配内存空间,然后将变量指向这个值的地址,比如运行a=1时解释器将变量指向整形值1的地址,那么当我们改变变量值的时候解释器又会给新的值分配另一个内存空间,再将变量指向这个新值的地址,比如运行a=0.1时解释器将变量指向浮点值0.1的地址。所以和C语言相比,在Python中改变的是变量所指向的地址,而内存空间中的值是固定不变的。下面通过具体的例子来看下Python动态类型语言的特点。
我们可以通过id()方法查看变量的内存地址的方式来进行验证。以下先以Python的int类型为例,可以看到执行 i += 1 后,变量i的内存地址会发生变化,事实上 i += 1 并不是在原有变量i的地址上加1,而是重新创建一个值为6的int对象,变量i则引用了这个新的对象,因此当变量i和变量j的值相同时会指向同个内存地址 。同样以Python的float 类型为例也验证了这个变量存储管理的机制。
#例程代码:
#int
i = 5
print(i)) #结果为:5
print(hex(id(i))) #结果为:0xa26f880
#重新创建值为6的int对象
i += 1
print(i) #结果为:6
print(hex(id(i)))#结果为:0xa26f874
#指向数值5的内存地址
j = 5
print(j) #结果为:5
print(hex(id(j))) #结果为:0xa26f880
#float相同
i = 1.5
print(i) #结果为:1.5
print(hex(id(i))) #结果为:0x9e86c8c
i += 1
print(i) #结果为:2.5
print(hex(id(i)))#结果为:0x9e86cac
j = 1.5
print(j) #结果为:1.5
print(hex(id(j))) #结果为:0x9e86c8c
接下来以Python的list类型为例,可以看到list变量i在append之后,仍然指向同一个内存地址,而j、k的值虽然相同,但是指向的内存地址却不同。我们通过j = k 的赋值语句可以让j、k指向同一个内存地址,对j、k任意一个list变量进行修改,都会影响另外一个list变量的值。比如j变量append(4)时,同时对k变量也产生影响,查看j、k的内存地址,发现它们仍然指向了同个内存地址。
#例程代码:
#list
i = [1, 2, 3]
print(i) #结果为:[1, 2, 3]
print(hex(id(i))) #结果为:0xb73fa1acL
#append后仍指向同一内存地址
i.append(4)
print(i) #结果为:[1, 2, 3, 4]
print(hex(id(i))) #结果为:0xb73fa1acL
#j、k的值虽然相同,但指向的内存地址却不同
j = [1.5, 2.5, 3.5]
print(j) #结果为:[1.5, 2.5, 3.5]
print(hex(id(j))) #结果为:0xb6fed06cL
k = [1.5, 2.5, 3.5]
print(k) #结果为:[1.5, 2.5, 3.5]
print(hex(id(k))) #结果为:0xb6fed04cL
#赋值语句让j、k指向同一个内存地址
j = k
print(j) #结果为:[1.5, 2.5, 3.5]
print(hex(id(j))) #结果为:0xb6fed04cL
print(k) #结果为:[1.5, 2.5, 3.5]
print(hex(id(k))) #结果为:0xb6fed04cL
#j、k任意一个list变量修改,会影响另外一个list变量的值
j.append(4)
print(j) #结果为:[1.5, 2.5, 3.5, 4]
print(hex(id(j))) #结果为:0xb6fed04cL
print(k)#结果为:[1.5, 2.5, 3.5, 4]
print(hex(id(k))) #结果为:0xb6fed04cL
刚才讲到Python的int类型的两个变量值相同时,Python解释器并不会分别为两个变量申请内存,而是优化的将他们指向同个内存地址。但在Python2.7里有个例外的情况,当变量s1和s2 存储20个char时,Python仍然将两个变量引用指向了相同内存地址,但当存储为21个char时,Python为s2开辟了新的内存,该情况在Python3.7.1版本中并不存在。
#例程代码:
#Python解释器优化情况
s1 = 'a' * 20
s2 = 'a' * 20
print(hex(id(s1)), hex(id(s2))) #结果为:0xb7075320L 0xb7075320L
s1 = 'a' * 21
s2 = 'a' * 21
print(hex(id(s1)), hex(id(s2))) #结果为:0xb70752f0L 0xb7075350L(注:Python3.7.1中不存在)
总结
根据以上的这些例子,我们可以知道实际上Python中数据类型对象分为可变类型和不可变类型,列表、字典是可变类型,而整数、浮点、短字符串、元组等是不可变类型。可变类型的变量赋值与我们了解的C语言机制相同,而不可变类型的变量赋值时,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象,当然如果没有其他变量引用原有对象时,原有对象就会被回收,这就是Python作为动态类型语言的特点。