第2章 变量、表达式与语句

2.1 值与类型

值是程序的基本组成要素,如一个字母或一个数字。目前为止,我们接触到的值有1、2和“Hello,World!”。

这些值属于不同的类型:2是整数,’Hello,World!’是字符串(包含一串字母)。你(或者解释器)可以根据有无引号来判别是否是字符串。

print语句也可以打印整数。输入python命令启动解释器。

python
>>> print 4
4

如果不确定一个值属于哪种类型,解释器会告诉你。

>>> type('Hello, World!')
<type 'str'>
>>> type(17)
<type 'int'>

显而易见,字符串属于str类型,整数属于int类型。带小数点的数字使用浮点(floating-point)格式表示,称为float类型。

>>> type(3.2)
<type 'float'>

那么,像'17'和'3.2'这种属于哪种类型呢?看起来像数字,但它们和字符串一样被放在单引号里面。

>>> type('17')
<type 'str'>
>>> type('3.2')
<type 'str'>

它们是字符串。

输入较大的数字时,可能会在每三个数字之间加一个逗号,例如,1,000,000。在Python中这不是一个整数,但这样表示是合法的。

>>> print 1,000,000
1 0 0

这不是我们想要的!Python解释器会把1,000,000当做一个逗号分隔的数字序列,它会把三部分依次打印出来,中间用空格分开。

这是我们遇到的第一个语义错误例子:代码运行不会输出任何错误信息,但是它并没有做“正确的事“。

2.2 变量

编程语言最强大的功能之一体现在对变量的操作能力。变量是一个值的引用名称。

赋值语句用来创建新变量并对其赋值:

>>> message = 'And now for something completely different'
>>> n = 17
>>> pi = 3.1415926535897931

这个例子列举了三种不同类型的赋值语句。第一条语句将字符串赋值给变量message;第二条语句将整数17赋值给变量n,第三条语句将π的近似值赋值给变量pi。

使用打印语句输出这些变量的值。

>>> print n
17
>>> print pi
3.14159265359

变量的类型就是它所指向的值的类型。

>>> type(message)
<type 'str'>
>>> type(n)
<type 'int'>
>>> type(pi)
<type 'float'>

2.3 变量名与关键字

程序员通常会选择有意义的变量名,通过变量名较为直观地了解变量的用途。

变量名不限长度,可以同时包含字母和数字,但是必须以字母开头。使用大写字母也是合法的,但使用小写字母会更好(随后会解释原因)。

下划线可以出现在变量名中。它经常用在含有多个词的变量名中,例如,my_name和airspeed_of_unladen_swallow。变量名可以采用下划线开头,但一般要避免这样命名,除非是编写供他人使用的Python库代码。

如果使用不合法的变量名,你就会得到一个语法错误:

>>> 76trombones = 'big parade'
SyntaxError: invalid syntax
>>> more@ = 1000000
SyntaxError: invalid syntax
>>> class = 'Advanced Theoretical Zymurgy'
SyntaxError: invalid syntax

76trombones不是合法的变量名,它不是以字母开头的。more@也是不合法的,它包含了一个不合法的字符@。变量名class错在哪呢?

原因在于,class是Python的保留关键字。Python解释器使用保留关键字来识别程序的结构,因此,保留关键字不能用于变量名。

Python的31个保留关键字如下所示1

and       del       from      not       while
as        elif      global    or        with
assert    else      if        pass      yield
break     except    import    print
class     exec      in        raise
continue  finally   is        return
def       for       lambda    try

你可以在手边存留一份。如果解释器指出了一个变量名不合法,而你又不知道为什么,那么检查一下变量名是否在这个列表里面。

2.4 语句

语句是Python解释器能够执行的代码单元。我们已经见到过两个语句:print和assignment。

在交互模式中输入一条语句,解释器就会执行它并打印出结果(如果有结果的话)。

程序举例如下:

print 1
x = 2
print x

得到以下输出结果:

1
2

其中,赋值语句没有输出结果。

2.5 运算符和运算对象

运算符是表示运算的一类特殊符号,例如,加法与乘法。运算符操作的值称为运算对象。

+、-、*、/和**五个运算符分别代表加、减、乘、除和次方的运算,请看如下示例:

20+32   hour-1   hour*60+minute   minute/60   5**2   (5+9)*(15-7)

除运算的结果可能不是你所期待的:

>>> minute = 59
>>> minute/60
0

minute的值是59,在传统的算术中,59除以60是0.98222,而不是0。出现这种差异的原因在于,Python执行的是地板除法(floor division)2

当两个运算对象都是整数时,那么结果也是整数。地板除法默认去掉小数部分,因此上面的运算结果是0。

如果两个运算对象都是浮点数,那么Python会执行浮点除法,结果就是浮点数:

>>> minute/60.0
0.98333333333333328

2.6 表达式

表达式是值、变量和运算符的组合。值本身可以是一个表达式,变量亦如此。下面都是合法的表达式(假设变量x已被赋值):

17
x
x + 17

如果在交互模式中输入一个表达式,解释器就会执行它并把结果打印出来。

>>> 1 + 1
2

然而,在一个程序中,表达式本身并不能做任何事情!这是初学者容易混淆的一点。

习题2.1 在Python解释器中输入下面的语句并查看结果:

5
x = 5
x + 1

2.7 运算顺序

当一个表达式中出现多个运算符时,运算顺序按照一定规则进行。对于数学运算符来说,Python遵照数学运算习惯,“先括号再次方接着乘除后加减”(PEMDAS)会帮助你记住运算顺序。

  • 括号拥有最高运算优先级,让表达式按特定顺序执行运算。括号内的表达式优先进行运算,例如,2 (3-1) 等于4,(1+1)**(5-2) 等于8。有时候,括号即便没有改变运算结果,但可以阅读起来更加方便,例如,(minute 100) / 60。
  • 幂运算的级别仅次于括号,例如,2**1+1 等于3,而不是4,3*1**3等于3,而不是 27。
  • 乘法和除法具有相同的优先级,高于加法和减法。例如,2*3 -1等于5,而不是 4,6+4/2等于8,而不是5。
  • 相同优先级的运算符按从左到右的顺序依次运算。例如,5-3-1等于1,而不是3。先计算5-3得到的2,然后再减1。

当不能确定运算顺序时,通常使用括号来确保既定的运算顺序。

2.8 模运算

模的运算对象是整数,得到的是第一个整数与第二个整数相除的余数。在Python中,模运算符用百分号(%)表示,语法与其他运算符一致:

>>> quotient = 7 / 3
>>> print quotient
2
>>> remainder = 7 % 3
>>> print remainder
1

7被3所除的商是2,余数是1。

模运算非常实用。举例来说,你可以检验一个数是否能被另一个数整除,如果x%y的结果是0,那么x能被y整除。

另外,模运算也可以提取出一个数字最右边的数位。举例来说,x%10可以提取出一个数最右边的一位数字(以10为基数)。同理,x%100可以提取出一个数最右边的两位数字。

2.9 字符串运算符

加号的运算对象是字符串,它会把字符串首尾相连。这里不是数学意义上的加号。例如:

>>> first = 10
>>> second = 15
>>> print first+second
25
>>> first = '100'
>>> second = '150'
>>> print first + second
100150

这个程序的输出结果是100150。

2.10 请求用户输入

有时候我们希望获取用户通过键盘输入的值。Python提供了一个输入函数raw_input,用来获取键盘输入3。当调用这个函数时,程序会暂停运行,等待用户的输入。当用户按下回车键时,程序就恢复运行,raw_input函数以字符串形式返回用户输入的值。

>>> input = raw_input()
Some silly stuff
>>> print input
Some silly stuff

从用户处获取输入之前,最好打印一条提示语句,告诉用户可以输入些什么。在程序暂停等待用户输入之前,你可以在raw_input中插入一个字符串来提示用户。

>>> name = raw_input('What is your name?\n')
What is your name?
Chuck
>>> print name
Chuck

提示语结尾的 \n 表示新开辟一行,它是一个用于换行的特殊字符。这样一来,用户输入的位置出现在提示语句的下面。

如果希望用户输入一个整数,你可以尝试int()函数,将返回的值转换成整数型:

What...is the airspeed velocity of an unladen swallow?
17
>>> int(speed)
17
>>> int(speed) + 5
22

但是,如果用户输入的不是数字组成的字符串,那么就会报错:

>>> speed = raw_input(prompt)
What...is the airspeed velocity of an unladen swallow?
What do you mean, an African or a European swallow?
>>> int(speed)

随后会介绍如何处理这类错误。

2.11 注释

当程序变得越来越复杂,阅读难度也随之增大。正规的程序代码很密集,经常会遇到看不懂这段代码是做什么的,或者为什么要这样写。

为解决这个问题,在程序代码中加入自然语言说明,解释这段代码的作用,这会是一个不错的主意。这些说明称为注释,它们以#号开头:

# compute the percentage of the hour that has elapsed
percentage = (minute * 100) / 60

上面这种情况,注释本身占一行。你也可以把它加到一行代码的末尾:

percentage = (minute * 100) / 60     # percentage of an hour

从#号开始到这一行的最后,编译时都会被忽略掉,它们不会对程序产生任何影响。

对代码不显著的特征进行说明时,注释非常有用。注释会帮助代码阅读者搞清楚这段代码的作用。注释用于代码为什么这样写的解释,同样有用。

下面这行注释明显是多余的,没什么作用:

v = 5     # assign 5 to v

而下面的这行注释则包含了有用的信息:

v = 5     # velocity in meters/second.

清晰易懂的变量名能够减少注释的使用,但是变量名如果太长,就会使复杂的表达式变得更加难懂,所以需要权衡利弊。

2.12 选择易记的变量名

只要遵循变量命名的简单规则,避免使用保留字,变量的命名还是有很多种选择的。编程入门阶段,你在阅读别人的程序和编写自己的程序时,对变量的命名可能会感到困惑。例如,下面三个程序实质上是一样的,但是阅读和理解起来差别很大。

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解释器对这三个程序的处理方式完全一样,但是对于读者理解起来差异很大。读者能够快速看懂的是第二个程序,这是因为程序员选择了能够代表变量取值含义的变量名。

这种变量命名法称为“助记变量命名法”。助记4的意思就是帮助记忆。选择易于记忆的变量名,有助于我们记住当初创建这个变量是为了做什么。

这看起来不错,使用助记变量命名法是一个好主意,但可能也会对初学者解析与理解代码上产生负面影响。由于初学者可能还没有记全Python的31个保留字,如果变量名中包含太多描述性的词语,精心命名的变量看上去会像是Python语言的一部分,对初学者理解上造成干扰。

下面两行简单的Python代码实现了循环。循环将在第5章介绍,这里尝试猜猜这两行代码的含义:

for word in words:
    print word

执行上面的代码会发生什么?for、word、in等这几个词中哪些是保留字,哪些是变量名?Python能理解单词的基本含义吗?初学者很难分辨出代码中哪些部分必须照抄示例中的,而哪些部分是可以自主选择的。

下面的代码和上面的代码实质上是一样的:

for slice in pizza:
    print slice

通过观察两段代码,初学者可以容易地分辨哪些是Python保留字,哪些是程序员自己定义的变量名。显而易见,Python不能理解pizza和slice之间的差别,实际上就是一个披萨可以切成很多块。

如果程序要读取数据并在数据中查找单词,pizza和slice是不容易记住的变量名。选择它们作为变量名会偏离程序的本意。

过不了多久,你会熟悉最常用的保留字并在程序中注意到它们:

for word in words:     print word

这段Python代码中for 、in 、print和:加粗显示,程序员自己定义的变量名不加粗。很多文本编辑器支持Python语法,它们会使用不同的颜色来标记保留字,让你能方便地区分保留字与变量名。熟悉一段时间后,你就会很快地区分哪些是保留字,哪些是变量名。

2.13 调试

最容易出现的语法错误是不合法的变量名,例如,class和yield是保留字,或者包含不合法字符的odd~job和US$。

如果变量名中存在空格,Python会认为它是没有运算符的两个运算对象:

>>> bad name = 5
SyntaxError: invalid syntax

语法错误的提示信息提供不了什么帮助。最常见的错误信息是SyntaxError: invalid syntax和 SyntaxError: invalid token,这两条都没有提供什么有价值的信息。

最常遇到的运行错误是“use before def;”,这个错误的意思是使用了一个还没有赋值的变量。这种情况容易出现在变量名拼写错误的情况:

>>> principal = 327.68
>>> interest = principle * rate
NameError: name 'principle' is not defined

变量名区分大小写,例如,LaTeX和latex是不一样的。

最容易出现的语义错误是运算顺序。举例来说,\(\dfrac{1}{2π} \)可能会写成这样:

>>> 1.0 / 2.0 * pi

这个语句首先进行除法运算,得到的结果是π/2,显然跟\( \dfrac{1}{2π} \)不是一回事!Python并不明白这个表达式是什么意思。这种情况下虽然不会报错,但计算结果是错误的。

2.14 术语

赋值:给变量赋予一个值的语句。

连接:将两个运算对象首尾相接。

注释:程序里面包含的信息,旨在帮助其他程序员(或者查看源代码的人)理解程序,不会对程序的执行产生任何影响。

求值:对表达式执行运算,得到一个值。

表达式:变量、运算符和值的组合,表示一个结果值。

浮点:带有小数部分的数值。

地板除法:截掉两数相除所得结果的小数部分的一种除法运算。

整数型:代表整数类型。

关键字:Python解释器用来解析程序的保留字。变量命名不可使用保留字,例如,if、def与while等。

助记法: 一种辅助记忆的方法。通常使用易记的变量名来帮助我们理解变量本身包含的内容。

模运算:用百分号(%)表示,求两数相除的余数。

运算对象:运算符操作的对象。

运算符:能够进行简单运算的一类特殊符号,例如,加法、乘法和字符串连接。

运算优先级:一组运算规则,用来规定多个运算符和运算对象的表达式中运算执行的次序。

语句:包含指令和行动的一段代码。目前为止,我们见到了赋值语句和打印语句。

字符串:由字符序列组成的数据类型。

类型:表示一类值。目前为止,我们见到了整数(int),浮点数(float)和字符串(str)。

值:数据的基本单位,例如,程序中可以操作的数字或字符串。

变量:一个值的引用名称。

2.15 练习

习题2.2 使用raw_input编写一个程序,提示用户输入姓名,然后打印出欢迎语。

Enter your name: Chuck
Hello Chuck

习题2.3 编写一个程序,让用户输入工时和时薪,然后计算出工资总额。

Enter Hours: 35
Enter Rate: 2.75
Pay: 96.25

暂时不用担心结果是否精确到小数点后两位。如果需要精确到小数点后两位,可以使用Python内置的round 函数。

习题2.4 执行下面的赋值语句:

width = 17
height = 12.0

写出以下表达式的值及值的类型。

  1. width/2
  2. width/2.0
  3. height/3
  4. 1 + 2 * 5

使用Python解释器检查你的结果是否正确。

习题2.5 编写一个程序,让用户输入摄氏温度,然后将其转化成华氏温度,最后将转化后的温度值打印出来。

1. Python 3.0中,exec不再是保留关键字,nonlocal是新的保留关键字。
2. Python 3.0中,做除法的结果是浮点数。新的运算符//做整数除法。
3. Python 3.0中,这个函数称为input。
4. "助记"的有关详细介绍,请参见http://en.wikipedia.org/wiki/Mnemonic