任何语言工具的变量都是以特定类型的形式存在的,Python 中也不例外。在 Python 中变量类型主要包括 Number(数字)、String(字符串)、List(列表)、Tuple(元组)和 Dictionary(字典)等,不过在 Python 中变量不需要预先声明类型就可以使用,这个即是所谓的动态类型特性,关于这个特点很多初学者都会难以理解,那么本小节就来讲解下 Python 中变量的类型及动态特性。
1. 变量类型的种类
刚才讲到 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
元组用 () 标识,内部元素用逗号隔开,类似于列表,但是与列表的区别在于元组不能二次赋值,相当于只读列表。
#例程代码:
#创建元组并初始化
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
字典用 { } 标识,由索引(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
2. 动态类型的特性
本小节第一部分中我们在创建数值变量例程中执行了 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 解释器并不会分别为两个变量申请内存,而是优化的将他们指向同个内存地址。但在 Python 2.7 里有个例外的情况,当变量 s1 和 s2 存储 20 个 char 时,Python 仍然将两个变量引用指向了相同内存地址,但当存储为 21 个 char时,Python 为 s2 开辟了新的内存,该情况在 Python 3.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中不存在)
3. 代码下载
本专栏相关代码 GitHub 地址:Quantitative-Trading
本节相关代码下载地址:Chapter04
4. 小结
根据以上的这些例子,我们可以知道实际上 Python 中数据类型对象分为可变类型和不可变类型,列表、字典是可变类型,而整数、浮点、短字符串、元组等是不可变类型。可变类型的变量赋值与我们了解的 C 语言机制相同,而不可变类型的变量赋值时,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象,当然如果没有其他变量引用原有对象时,原有对象就会被回收,这就是 Python 作为动态类型语言的特点。