Python 语言的基础构成要素:变量与数据类型。因为不仅是 Python,变量与数据类型可以说是所有编程语言的核心。掌握了语言的变量与数据类型的体系,可以说就掌握了这门语言的一大半儿。
1、什么是变量
我们都知道应用程序在运行阶段的数据存放在内存中,那实现应用程序的编程语言是怎么操作内存的呢?
比如我们通过程序语言申请了一块内存空间,存入了一个数字,那下次我想读取这个数字的时候,面临的问题就是:如何告诉操作系统"我想读取的是刚才我存了数字的那块内存"。
上面的问题,从直觉出发,我们很容易想到可以在申请空间和存入数字的时候,给这块内存区域起一个名字,比如叫我的内存 1 ,然后当我想读取这个部分的值的时候,只需要告诉操作系统,请读取我的内存 1名字对应的内存里的值。
编程语言中所谈到的变量,其实就是上面例子中提到的,给某块内存区域起的名字。这也是变量为什么有一个"变"字,因为这个名字对应的内存区域里面的值是可以变化的。
变量对应的内存区域的内容,一般称为变量的值。在 Python 中,变量的命名一般使用"字母+数字"的形式来表示,最好使用符合变量含义的英文单词,这样方便理解。比如存储地址的变量可以叫:address。
一个变量不仅存储的值可以变化,就连数据类型也是可以变化的。比如同样叫我的内存 1,这会儿存的还是数字,但下次我也可以存一个字符串进去。
接下来,我们通过实操来感受一下变量的含义。
打开 VS Code,建立一个新的 Notebook, 保存为 Chapter2.ipynb。
第一步,新建一个代码的 Cell,输入以下代码。
ini
a1 = "Hello Data Science"
a2 = 135
在这段代码中,我们通过赋值的形式创建了两个变量,一个叫 a1,存储了一个字符串:"Hello Data Science",一个叫 a2, 存储了一个数字:135。
等于符号("=")在 Python,甚至绝大多数编程语言中都代表赋值,代表把等号右边的值存储到等号左边的变量中,这里和数学中的等式中的等号含义是不一样的,需要注意。
运行该 Cell,由于我们没有 print 语句的调用,所以这个 Cell 不会输出任何内容。但运行之后, a1 和 a2 这两个变量在整个 Notebook 中都会是有效的。在之后的代码中,我们都可以直接访问 a1 和 a2 的值。接下来我们马上试一下。
第二步,在新的 Cell 中输入 a1, 直接运行,可以看到打印出了我们第一步存储的字符串。
这里虽然没有 print, 为什么仍然能够打印变量的值呢? 在 Notebook 中,如果一个 Cell 最后一行是一个单独变量的话, Notebook 会自动打印出该变量的值。同理我们输入 a2 并运行,可以看到打印了 135。如下图所示。
第三步,新建一个 Cell,输入以下代码。
ini
a2 = 100
a3 = 135
print(a2 + a3)
在上面的代码中,首先修改了变量 a2 的值为 100,然后新创建了一个变量 a3 并存储了 135 这个数字。最后,打印了 a2 + a3 的值。运行 Cell,可以看到打印了 235。
上面的例子演示了变量的创建、查看、修改与打印。完整的 Notebook Cell 如下图所示。
从这个例子我们可以更清楚地明白,变量首先是一个名字,是某块内存区域的名字。我们可以给通过等号变量赋值,即把数据存储到变量中,也可以从变量读取目前存储的值。变量的创建和修改都是通过赋值实现的(即等号)。最后,我们可以通过 Python 的 print 语句打印出变量的值,通过这个方式来查看变量中存储的值。
2、数据类型
在刚才的例子中,a1("Hello Data Science")是一个字符串,而 a2(100)、a3(135)是整数。这里提到的"字符串""整数"就代表变量的数据类型。在编写代码的时候,搞清楚变量的数据类型至关重要。
为什么呢? 下面通过一个例子来说明。
还记得在刚才的 Notebook 中,我们最后计算了 a2 + a3 的值,由于 a2 和 a3 都是整数,所以 Python 算出了结果。但如果是一个字符串和一个整数呢?
让我们来试一下,在刚才的 Notebook 新建一个 Cell,输入 print(a1 + a2)并运行,运行结果如下图所示。程序执行出错,提示"字符串只可以与字符串拼接,而不能和整数"。
这背后的原因是:加运算对于整数类型和字符串类型的含义是不一样的,对于整数来说,加是做加法,而对于字符串,加号则代表将两个字符串拼接起来。所以搞清楚不同变量的数据类型,以及运算过程(运算符和函数)对于参与运算的变量的数据类型的要求,是能够写出正确的 Python 代码的基础。
在逐一介绍数据类型之前,先介绍一个实用的技巧:查看变量的类型。掌握这个技巧可以在后续的环节帮助我们更好地理解数据类型。
3、查看变量的类型
在实际工作中,我们看到的经常是多个变量,并不能明显看出它到底是整型还是浮点型。那如何查看一个变量的类型呢?Python 中可以使用 type 语句来查看一个变量的类型,使用方法和 print 语句一样,只是括号内部不是字符串,而是变量名。
新建一个 Cell,输入如下代码并运行。
bash
print(type(a2))
代码的含义是:我们首先用 type 语句获得 a2 的类型,然后再用 print 语句打印出来。
代码运行后,输出在 Cell 下方的内容如下所示。这里的 class 代表我们打印的是一个类型,类型的名字是:int,也就是整型,往上看我们的代码。a2 确实是整型。所以结果是符合预期的。
像 type 和 print 这样的特殊的 python 语句,叫作函数,大家只需要先对这个概念有个感性的认识,具体下一篇会展开来介绍。这里只需要掌握其用法即可。
接下来,我们就来看看 Python 的常见的数据类型:整型、浮点型、布尔型、字符串、列表。
(1)整型
整型可以简单理解成:整数类型,英文名叫 int,也有很多语言叫作 Integer。像刚才例子中的 a2 和 a3 都是整型。
(2)浮点型
浮点型一般用来表达小数,英文名叫 float。在数值计算中,小数比整数应用的场景更加广泛。
比如我们希望通过数据分析来预测用户点击某一类商品的概率,就很显然需要使用小数(浮点型)。这里我们可以一起操作下。
新建 Cell,输入以下代码。
ini
a4 = 10 / 8
a5 = a4 * 2
a6 = a5 / 3
print(a4, type(a4), a5, type(a5), a6, type(a6))
上述代码中,我们做了这几件事。
计算了 10 除以 8 的值,并将结果存入了变量 a4;
计算了 a4 乘以 2 的值,并将结果存入了变量 a5;
计算了 a5 除以 3 的值,并将结果存入了变量 a5;
打印上述三个变量的值域类型。
输出如下所示,可以看到,三个变量的类型都是 float,代表都是浮点数。在输出的格式中,也有小数的部分,其中对于无理数, Python 会取前 16 位展示。
arduino
1.25 <class 'float'> 2.5 <class 'float'> 0.8333333333333334 <class 'float'>
(3)布尔型
布尔型代表变量只可能有两种取值:True / False,英文名叫 Bool 或者 Boolean,一般用来存储逻辑判断的结果。True / False 用中文的理解可以是真或假、是或否。在数据分析中,我们经常需要根据一定的条件筛选出数据,比如我们希望从一个班的成绩表中,筛选出大于 90 分的学生,那我们需要看每一个学生,他的成绩"是否大于 90"。 这里判断一个分数"是否大于 90"的结果,就是一个布尔型变量。
我们还是直接基于代码来理解。新建 Cell, 输入以下代码:
ini
stu_score = 93
a7 = stu_score > 90
a8 = stu_score < 60
a9 = a7 and a8
a10 = a7 or a8
print(a7, type(a7), a8, type(a8), a9, type(a9), a10, type(a10))
代码和上面一节的结构很像,主要做了:
创建了一个整型变量 stu_score 并赋值 93;
将逻辑判断 stu_score 是否大于 90 的结果存到了变量 a7;
将逻辑判断 stu_score 是否小于 60 的结果存到了变量 a8;
对 a7 和 a8 进行逻辑"与"运算,并将结果存到 a9;
对 a7 和 a8 进行逻辑"或"运算,并将结果存到 a10;
打印 a7a8
a9``a10 的值和类型。
输出结果如下所示:
python
True <class 'bool'> False <class 'bool'> False <class 'bool'> True <class 'bool'>
可以看到变量 a7 和 a8 的值都为 bool 类型,也就是布尔型。其中 a7 为真,a8 为假,a9 为假,a10 为真。 以上,我们学习了布尔类型的基础以及针对布尔类型的基本运算:逻辑运算。Python 中,用 and 表示逻辑的与运算,代表需要两个操作数都为真,结果才为真。or 表示逻辑的或运算,代表两个操作数只要有其中一个为真,则结果就为真。
(4)字符串
字符串,顾名思义,是用来描述、存储和表达文本的数据类型,是计算机领域一个非常重要的数据类型。我们在电脑上写的文章,微信聊天中发送的消息,背后都是一个个字符串类型的数据。
Python 中,字符串代表用单引号或者双引号包住的内容,比如 "Hello Data Science"就是一个字符串,字符串由一对双引号内的一个个字符组成(空格也算一个字符)。
在实际工作场景中,我们处理的数据表往往不全是数字,比如学生信息表,除了性别、年龄之外,像姓名,籍贯,家庭住址等信息,都是通过字符串来存储的,所以掌握字符串的使用对于数据分析也至关重要。
接下来,我们通过几个简单的例子来感受字符串的特性与基本的使用。
①拼接。在 Python 中,对于字符串类型的值和变量,加号代表拼接,就是把两个字符串拼接到一起。新建 Cell, 输入以下代码:
ini
a1 = "Hello, Data Science"
a2 = "I'm coming"
a3 = a1 + ", " + a2
print(a1, type(a1))
print(a3, type(a3))
输出结果是这样的:
arduino
Hello, Data Science <class 'str'>
Hello, Data Science, I'm coming <class 'str'>
可以看到,第二行的字符串已经是两个字符串 a1 和 a3 拼接起来的做过,并且我们还在中间增加了一个逗号。
②换行。如果字符串太长,我们希望在打印出来的时候能够换行显示,可以在字符串中插入 '\n' 字符。这样,最终打印的时候 '\n' 之后的字符都会在新的一行显示。示例如下:
swift
a1 = "Hello\nData Science"
print(a1, type(a1))
输出结果是这样的:
arduino
Hello
Data Science <class 'str'>
可以看到,在 \n 后面的 Data Science 被显示到了新的一行。
总结一下,字符串代表字符的集合,是用来存储文本的数据类型。两个字符串类型的变量可以用相加,代表把两个字符串拼接在一起。另外,当字符串包含\n 这样的换行符时,我们通过 print 函数打印该字符串时,\n 字符后面的内容会在新的一行显示,\n 字符本身并不会显示。
(5)列表
列表和字符串一样,都是存储多个值的集合。与字符串不同的是,字符串只能存储一个个字符,而列表则可以存储任意类型的变量。
比如我们可以有存储多个整型的列表,也可以有存储多个浮点型的列表。比如最简单的场景,要统计一个班 50 名同学数学考试的平均分,一般情况下都需要将 50 个分数存在列表,然后才能针对这个列表去算平均分。
与字符串一样,列表类型也有一系列函数用于操作自身,话不多说,咱们直接上 Notebook 演示列表的基本操作,主要包含:
列表的创建;
打印;
取列表长度;
取列表中的某个位置的变量;
列表中添加新的值;
除列表的某个值。
我们还是新建 Cell,输入以下代码。
go
tc_a1 = 10
la = [1,2,3,tc_a1]
print("List is: ", la)
print("List length: ", len(la))
print("List tail: ", la[-1])
print("List head: ", la[0])
la.append("Hello")
print("After append: ", la)
la.remove(2)
print("After remove: ", la)
在上面的代码中,我们主要完成了:
创建列表类型的变量 la,并且存入了 4 个值,分别是数字 1、2、3 和变量 tc_a1;
打印了变量 la;
打印了变量 la 的长度,通过 len 函数,这里和之前取字符串长度的方法是一样的;
打印了变量 la 的最后一个元素,对于列类型的变量,la[x]代表取列表中第 x 个元素的值,从 0 开始。当 x = -1 时,代表返回列表的最后一个元素;
打印变量 la 的第一个元素,原理同上;
通过列表类型的 append 函数,将字符串"Hello"添加到 la 中;
打印添加元素后的 la;
通过列表类型的 remove 函数,将数字 2 从 la 中删除;
打印删除元素后的 la。
最终运行 Cell,结果输出如下:
yaml
List is: [1, 2, 3, 10]
List length: 4
List tail: 10
List head: 1
After append: [1, 2, 3, 10, 'Hello']
After remove: [1, 3, 10, 'Hello']
通过以上示例,我们可以知道列表类型的基本使用方法。
创建:通过中括号来创建一个列表,中括号里面是一个或多个值,用逗号隔开,代表列表中的值。
获取长度:同字符串一样,可以通过 len 函数计算列表的长度(即列表中的元素的个数)。
获取元素:可以通过列表变量[x]的形式访问列表中的第 x 个值(从 0 开始算),如 la[0]代表列表 la 的第一个元素
增删元素:在创建完列表之后,可以通过调用列表的 append 添加元素,remove 函数元素。
4、类型转换
Python 本身也是支持整型和浮点型做混合运算的,怎么实现的呢?我们来做一个简单的试验。还是熟悉的新建 Cell, 输入以下代码:
ini
tc_a1 = 10
tc_a2 = 1.5
tc_a3 = tc_a1 + tc_a2
print(tc_a3, type(tc_a3))
我们将一个整数和一个浮点数分别存在变量 tc_a1 和 tc_a2 中,并且将其相加,把结果存在 tc_a3 中,运行之后,结果输出是 11.5:
arduino
11.5 <class 'float'>
可以看到最终结果被正确地算了出来,并且存储结果的变量类型为浮点型。
所以本质上来说,运算过程仍然是限定在同一类型中进行的,只是对于一部分类型,Python 会进行自动地类型转换,比如当一个整型和浮点型相加时,Python 会自动先将整型转换为浮点型,然后再做加法,所以最后的结果是浮点型。
为什么是整型自动转浮点而不是反过来呢? 核心原因是如果默认浮点转整型,则会造成精度的丢失。所以从代价上来看,整型转浮点更合适。
如果我们不希望用 Python 默认的类型转换,而是希望由我们来决定怎么转换该怎么办呢? 比如像刚才的例子,我就希望转成整数去做计算,精度丢失无所谓(现实生活中确实也存在这种场景,比如我们想统计大概人数,显然一般不存在几点几个人,所以这种情况我们倾向于直接转换为整数计算,抛弃小数部分)。
接下来,来看一下python的手动类型转换,还是新建 Cell,输入以下代码:
scss
tc_a4 = tc_a1 + int(tc_a2)
print(tc_a4, type(tc_a4))
我们仍然延续了上一个 Cell 的变量:整型tc_a1和浮点型tc_a2,与刚才直接相加不同的是,我们首先通过int函数将tc_a2强制转换成整型之后再做加法。运行之后,输出如下所示。
arduino
11 <class 'int'>
可以看到最终结果是 11,因为我们把tc_a2强制转换为整数的时候,丢失了精度(这里的丢失精度跟四舍五入没关系,只是把小数点后面的内容直接去掉了),所以最后计算的是 10 +1。
强制类型转换还有另一个经典场景就是将内容为数字的字符串转换为数字参与运算。与上一例类似,我们可以用类型函数进行转换。在下面的例子中我们用float函数将变量tc_a5从字符串转换为了浮点数.
ini
tc_a5 = "21.5"
tc_a6 = tc_a1 + float(tc_a5)
tc_a6
结果输出为31.5
31.5