变量、表达式和语句
值和类型
值(value)是程序处理的基本单元之一,比如一个字母或一个数字。到目前为止我们看到的值有 1、2 和 “Hello, World!”。
这些值属于不同的类型(type):2 是一个整数(integer),而 “Hello, World!” 是一个字符串(string),之所以这么叫是因为它包含一“串”(string)字母。你(和解释器)可以通过它们被引号括起来识别字符串。
print
语句也适用于整数。我们使用 python
命令来启动解释器。
python
>>> print(4)
4
如果你不确定一个值是什么类型,解释器可以告诉你。
>>> type('Hello, World!')
<class 'str'>
>>> type(17)
<class 'int'>
不出所料,字符串属于 str
类型,整数属于 int
类型。不那么显而易见的是,带小数点的数字属于一种称为 float
的类型,因为这些数字是以一种称为浮点(floating point)的格式表示的。
>>> type(3.2)
<class 'float'>
那么像 “17” 和 “3.2” 这样的值呢?它们看起来像数字,但像字符串一样用引号括起来了。
>>> type('17')
<class 'str'>
>>> type('3.2')
<class 'str'>
它们是字符串。
当你输入一个大整数时,你可能会想在三位数字组之间使用逗号,例如 1,000,000。这在 Python 中不是一个合法的整数,但它是合法的:
>>> print(1,000,000)
1 0 0
嗯,这完全不是我们所期望的!Python 将 1,000,000 解释为一个逗号分隔的整数序列,它在打印时在它们之间加上了空格。
这是我们看到的第一个语义错误的例子:代码运行没有产生错误消息,但它没有做“正确”的事情。
变量
编程语言最强大的特性之一是能够操作变量 (variable)。变量是一个指向值的名称。
一个赋值语句 (assignment statement) 创建新变量并给它们赋值:
>>> message = 'And now for something completely different'
>>> n = 17
>>> pi = 3.1415926535897931
这个例子进行了三次赋值。第一次将一个字符串赋给名为 message
的新变量;第二次将整数 17 赋给 n
;第三次将 π 的(近似)值赋给 pi
。
要显示变量的值,你可以使用 print 语句:
>>> print(n)
17
>>> print(pi)
3.141592653589793
变量的类型就是它所引用的值的类型。
>>> type(message)
<class 'str'>
>>> type(n)
<class 'int'>
>>> type(pi)
<class 'float'>
变量名和关键字
程序员通常为他们的变量选择有意义的名称,并用其说明变量的用途。
变量名可以任意长。它们可以包含字母和数字,但不能以数字开头。使用大写字母是合法的,但最好以小写字母开头变量名(稍后你会明白为什么)。
下划线字符( _ )可以出现在名称中。它经常用于包含多个单词的名称,例如 my_name
或 airspeed_of_unladen_swallow
。变量名可以以下划线字符开头,但我们通常避免这样做,除非我们正在为他人编写库代码。
如果你给变量起了一个非法的名称,你会得到一个语法错误:
>>> 76trombones = 'big parade'
SyntaxError: invalid syntax
>>> more@ = 1000000
SyntaxError: invalid syntax
>>> class = 'Advanced Theoretical Zymurgy'
SyntaxError: invalid syntax
76trombones
是非法的,因为它以数字开头。more@
是非法的,因为它包含非法字符 @。但是 class
有什么问题呢?
原来 class
是 Python 的关键字之一。解释器使用关键字来识别程序的结构,它们不能用作变量名。
Python 保留了 35 个关键字:
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
你可能想把这个列表放在手边。如果解释器抱怨你的某个变量名而你不知道原因,看看它是否在这个列表上。
语句
语句 (statement) 是 Python 解释器可以执行的一段代码单元。我们已经看到了两种语句:print(一种表达式语句)和赋值语句。
当你在交互模式下输入一条语句时,解释器会执行它并显示结果(如果有的话)。
一个脚本通常包含一系列语句。如果有多条语句,结果会随着语句的执行逐一出现。
例如,脚本
print(1)
x = 2
print(x)
产生输出
1
2
赋值语句不产生输出。
运算符和操作数
运算符 (Operators) 是表示像加法和乘法这类计算的特殊符号。运算符作用于的值被称为操作数 (operands)。
运算符 +
、-
、*
、/
和 **
分别执行加法、减法、乘法、除法和求幂运算,如下例所示:
20+32
hour-1
hour*60+minute
minute/60
5**2
(5+9)*(15-7)
在 Python 2 和 Python 3 之间,除法运算符发生了一个变化。在 Python 3 中,这种除法的结果是一个浮点数结果:
>>> minute = 59
>>> minute/60
0.9833333333333333
Python 2 中的除法运算符会去除两个整数相除结果的小数部分,得到一个整数:
>>> minute = 59
>>> minute/60
0
要在 Python 3 中获得相同的答案,请使用整除(//
)运算符。
>>> minute = 59
>>> minute//60
0
在 Python 3 中,整数除法的行为更符合你在计算器上输入表达式时的预期。
表达式
表达式 (expression) 是值、变量和运算符的组合。一个单独的值本身被认为是一个表达式,一个变量也是,所以以下都是合法的表达式(假设变量 x
已被赋值):
17
x
x + 17
如果你在交互模式下输入一个表达式,解释器会对其进行求值 (evaluates) 并显示结果:
>>> 1 + 1
2
但在脚本中,一个单独的表达式本身不做任何事情!这是初学者常见的困惑来源。
练习 1: 在 Python 解释器中输入以下语句,看看它们的作用:
5
x = 5
x + 1
运算顺序
当一个表达式中出现多个运算符时,求值顺序取决于优先级规则 (rules of precedence)。对于数学运算符,Python 遵循数学惯例。缩写词 PEMDAS 是一个记住规则的好方法:
Parentheses (括号):优先级最高,可以用来强制表达式按你想要的顺序求值。因为括号中的表达式首先被求值,所以
2 * (3-1)
是 4,而(1+1)**(5-2)
是 8。你也可以使用括号使表达式更易读,如(minute * 100) / 60
,即使它不改变结果。Exponentiation (求幂):具有次高优先级,所以
2**1+1
是 3 而不是 4,3*1**3
是 3 而不是 27。Multiplication (乘法) 和 Division (除法):具有相同的优先级,高于 Addition (加法) 和 Subtraction (减法),后者也具有相同的优先级。所以
2*3-1
是 5 而不是 4,6+4/2
是 8 而不是 5。具有相同优先级的运算符从左到右求值。所以表达式
5-3-1
的结果是 1,而不是 3,因为5-3
先发生,然后从 2 中减去1
。
如有疑问,始终在表达式中使用括号,以确保计算按你期望的顺序执行。
模运算符
模运算符 (modulus operator) 作用于整数,并产生第一个操作数除以第二个操作数后的余数。在 Python 中,模运算符是百分号(%
)。其语法与其他运算符相同:
>>> quotient = 7 // 3
>>> print(quotient)
2
>>> remainder = 7 % 3
>>> print(remainder)
1
所以 7 除以 3 等于 2 余 1。
模运算符出乎意料地有用。例如,你可以检查一个数是否能被另一个数整除:如果 x % y
为零,那么 x
可以被 y
整除。
你还可以从一个数中提取最右边的数字或几位数字。例如,x % 10
得到 x
的最右边一位数字(以 10 为基数)。类似地,x % 100
得到最后两位数字。
字符串操作
+
运算符适用于字符串,但这并非数学意义上的加法。它执行的是拼接 (concatenation),意味着通过将字符串首尾相连来连接它们。例如:
>>> first = 10
>>> second = 15
>>> print(first+second)
25
>>> first = '100'
>>> second = '150'
>>> print(first + second)
100150
*
运算符也适用于字符串,通过将字符串的内容乘以一个整数来实现。例如:
>>> first = 'Test '
>>> second = 3
>>> print(first * second)
Test Test Test
向用户请求输入
有时我们希望通过用户的键盘来获取变量的值。Python 提供了一个名为 input
的内置函数,用于从键盘获取输入 1。当调用此函数时,程序会停止并等待用户输入内容。当用户按下 Return
或 Enter
键时,程序继续运行,input
函数返回用户输入的内容(作为字符串)。
>>> inp = input()
Some silly stuff
>>> print(inp)
Some silly stuff
在从用户处获取输入之前,最好打印一个提示,告诉用户要输入什么。你可以向 input
传递一个字符串,在暂停等待输入之前显示给用户:
>>> name = input('What is your name?\n')
What is your name?
Chuck
>>> print(name)
Chuck
提示末尾的序列 \n
代表一个换行符 (newline),它是一个导致换行的特殊字符。这就是为什么用户的输入出现在提示下方的原因。
如果你期望用户输入一个整数,你可以尝试使用 int()
函数将返回值转换为 int
:
>>> prompt = 'What...is the airspeed velocity of an unladen swallow?\n'
>>> speed = input(prompt)
What...is the airspeed velocity of an unladen swallow?
17
>>> int(speed)
17
>>> int(speed) + 5
22
但是如果用户输入的不是一串数字,你会得到一个错误:
>>> speed = input(prompt)
What...is the airspeed velocity of an unladen swallow?
What do you mean, an African or a European swallow?
>>> int(speed)
ValueError: invalid literal for int() with
base 10: 'What do you mean, an African or a European swallow?'
我们稍后会看到如何处理这类错误。
注释
随着程序变得越来越大和复杂,它们也变得越来越难读。形式语言是密集的,通常很难看着一段代码就能弄清楚它在做什么,或者为什么这样做。
因此,在你的程序中添加注释以用自然语言解释程序在做什么是一个好主意。这些注释被称为注释 (comments),在 Python 中它们以 #
符号开头:
# 计算已过去的小时百分比
percentage = (minute * 100) / 60
在这种情况下,注释单独占一行。你也可以将注释放在代码行的末尾:
percentage = (minute * 100) / 60 # 小时的百分比
从 #
到行尾的所有内容都会被忽略;它对程序没有影响。
当注释记录了代码中不明显的特性时,它们最有用。假设读者能弄清楚代码做了什么是合理的;解释为什么这样做更有用。
这个注释与代码重复,是无用的:
v = 5 # 将 5 赋给 v
这个注释包含了代码中没有的有用信息:
v = 5 # 速度,单位:米/秒。
好的变量名可以减少对注释的需求,但是长名称会使复杂的表达式难以阅读,所以需要权衡。
选择助记的变量名
只要你遵循简单的变量命名规则,并避免使用保留字,你在命名变量时就有很多选择。在开始时,无论是在阅读程序还是编写自己的程序时,这种选择都可能令人困惑。例如,以下三个程序在它们完成的功能方面是相同的,但在你阅读并试图理解它们时却非常不同。
a = 35.0
b = 12.50
c = a * b
print(c)
hours = 35.0
rate = 12.50
pay = hours * rate
print(pay)
x1q3z9ahd = 35.0
x1q3z9afd = 12.50
x1q3p9afd = x1q3z9ahd * x1q3z9afd
print(x1q3p9afd)
Python 解释器将这三个程序视为完全相同,但人类看待和理解这些程序的方式却大相径庭。人类会最快地理解第二个程序的意图,因为程序员选择了能够反映他们打算在每个变量中存储什么数据的变量名。
我们称这些明智选择的变量名为“助记变量名”(mnemonic variable names)。单词 mnemonic 2 的意思是“记忆辅助”。我们选择助记变量名是为了帮助我们记住当初创建该变量的原因。
虽然这听起来很棒,并且使用助记变量名是一个非常好的主意,但助记变量名可能会妨碍初学者解析和理解代码的能力。这是因为初学者还没有记住保留字(总共只有 35 个),有时名称过于描述性的变量开始看起来像是语言的一部分,而不仅仅是选择得好的变量名。
快速看一下下面这段遍历某些数据的 Python 示例代码。我们很快会学习循环,但现在试着理解它的意思:
for word in words:
print(word)
这里发生了什么?哪些标记(for、word、in 等)是保留字,哪些只是变量名?Python 是否从根本上理解“单词”的概念?初学者很难区分代码的哪些部分必须与此示例相同,哪些部分只是程序员的选择。
以下代码与上面的代码等效:
for slice in pizza:
print(slice)
对于初学者来说,看这段代码更容易知道哪些部分是 Python 定义的保留字,哪些部分只是程序员选择的变量名。很明显,Python 对披萨和切片,以及披萨由一片或多片组成这一事实没有基本的理解。
但是,如果我们的程序真的是关于读取数据并在数据中查找单词,那么 pizza
和 slice
是非常不助记的变量名。选择它们作为变量名会偏离程序的含义。
经过相当短的一段时间后,你将熟悉最常见的保留字,并且你会开始看到保留字在你眼前“跳”出来:
for word in words:
print(word)
代码中由 Python 定义的部分(for
、in
、print
和 :
)是粗体的,而程序员选择的变量(word
和 words
)则不是粗体。许多文本编辑器都了解 Python 语法,并将用不同的颜色显示保留字,为你提供区分变量和保留字的线索。一段时间后,你将开始阅读 Python 并快速确定什么是变量,什么是保留字。
调试
此时,你最可能犯的语法错误是非法变量名,例如 class
和 yield
(它们是关键字),或者 odd~job
和 US$
(它们包含非法字符)。
如果你在变量名中加入空格,Python 会认为它是两个没有运算符的操作数:
>>> bad name = 5
SyntaxError: invalid syntax
对于语法错误,错误消息帮助不大。最常见的消息是 SyntaxError: invalid syntax
,这信息量不大。
你最可能犯的运行时错误是“先用后定义(use before def)”,即在给变量赋值之前就尝试使用它。如果你拼错了变量名,就可能发生这种情况:
>>> principal = 327.68
>>> interest = principle * rate
NameError: name 'principle' is not defined
变量名是区分大小写的,所以 LaTeX
与 latex
不同。
此时,语义错误最可能的原因是运算顺序。例如,要计算 1/2 π,你可能会想写成
>>> 1.0 / 2.0 * pi
但是除法会先发生,所以你会得到 π/2,这是不一样的东西!Python 无法知道你想要写什么,所以在这种情况下你不会收到错误消息;你只是得到了错误的答案。
术语表
赋值 (assignment)
给变量赋一个值的语句。
拼接 (concatenate)
将两个操作数首尾相连。
注释 (comment)
程序中为其他程序员(或任何阅读源代码的人)提供的信息,对程序的执行没有影响。
求值 (evaluate)
通过执行运算来简化表达式,以产生单个值。
表达式 (expression)
变量、运算符和值的组合,代表一个单一的结果值。
浮点数 (floating point)
表示带有小数部分的数字的类型。
整数 (integer)
表示整数的类型。
关键字 (keyword)
编译器用来解析程序的保留字;你不能使用像 if
、def
和 while
这样的关键字作为变量名。
助记的 (mnemonic)
记忆辅助。我们经常给变量起助记的名称,以帮助我们记住变量中存储的内容。
模运算符 (modulus operator)
用百分号(%
)表示的运算符,作用于整数,并产生一个数除以另一个数后的余数。
操作数 (operand)
运算符作用于的值之一。
运算符 (operator)
表示简单计算(如加法、乘法或字符串拼接)的特殊符号。
优先级规则 (rules of precedence)
规定包含多个运算符和操作数的表达式求值顺序的一套规则。
语句 (statement)
表示命令或操作的一段代码。到目前为止,我们看到的语句是赋值语句和 print 表达式语句。
字符串 (string)
表示字符序列的类型。
类型 (type)
值的类别。到目前为止我们看到的类型有整数(int
类型)、浮点数(float
类型)和字符串(str
类型)。
值 (value)
程序操作的基本数据单位之一,如数字或字符串。
变量 (variable)
指向一个值的名称。
练习
练习 2: 编写一个程序,使用 input
提示用户输入他们的名字,然后欢迎他们。
Enter your name: Chuck
Hello Chuck
练习 3: 编写一个程序,提示用户输入工作小时数和每小时费率来计算总工资。
Enter Hours: 35
Enter Rate: 2.75
Pay: 96.25
我们现在不用担心确保我们的工资在小数点后正好有两位数字。如果你愿意,可以尝试使用 Python 内置的 round
函数将结果工资精确到两位小数。
练习 4: 假设我们执行以下赋值语句:
width = 17
height = 12.0
对于以下每个表达式,写出表达式的值和类型(表达式值的类型)。
width//2
width/2.0
height/3
1 + 2 * 5
使用 Python 解释器检查你的答案。
练习 5: 编写一个程序,提示用户输入摄氏温度,将温度转换为华氏温度,并打印出转换后的温度。
- 在 Python 2 中,这个函数名为
raw_input
。 ↩︎ - 关于“mnemonic”一词的扩展描述,请参见 https://en.wikipedia.org/wiki/Mnemonic。 ↩︎
如果你在本书中发现错误,欢迎使用 Github 给我发送修正。