*1.8 数据与程序设计
虽然人类发明了构成现代计算机的数据表示和基本操作,但很少有人特别擅长于直接在计算机的这个层面上工作。人们喜欢在更高的抽象层次上推理计算问题,他们依赖计算机来处理最底层的细节问题。程序设计语言(programming language)是人类创造的一个计算机系统,通过它,人们能够使用更高层次的抽象向计算机精确地表达算法。
在20世纪,对计算机进行程序设计被认为是少数训练有素的专家们才能涉足的领域;当然,仍然有许多计算问题需要有经验的计算机科学家和软件工程师的关注。不过,到了21世纪,随着计算机和计算与我们现代生活中的方方面面交织在一起的程度的日益加深,更加难以确定哪些职业不需要至少有某种程度的编程技能。事实上,一些人已经把程序设计或编码确定为现代读写能力中继阅读、写作及算术之后的又一个基础支柱了。
在本节以及后续章节的程序设计补充部分,我们会看到程序设计语言是如何反映本章主要内容,以及如何让人类更容易地解决计算问题的。
1.8.1 Python入门
Python是一门程序设计语言,由吉多·范罗苏姆(Guido van Rossum)于20世纪80年代后期创立。现在它是十大最常用的语言之一,仍然深受网络应用开发领域及科学计算领域人士的喜爱,并被视为学生的入门语言。使用Python的组织,从谷歌到美国国家航空航天局(NASA),从DropBox公司到工业光魔公司(Industrial Light & Magic),范围很广;使用Python的计算机用户也横跨非正式、科学和艺术领域。Python强调可读性,包括命令型程序设计范型、面向对象型程序设计范型和函数式程序设计范型等三大要素,这些将在第6章中介绍。
用于编辑和运行用Python编写的程序的软件可以免费从www.python.org获得,这里还有很多其他的入门资源。Python语言一直在不断演变,本书的所有示例都将使用Python 3这个版本。更早的Python版本能够运行非常类似的程序,但是自Python 2版本开始有了许多细微的变化,如标点符号。
Python是一种解释型语言(interpreted language),这意味着初学者可以将Python指令键入交互提示符中,或者将Python指令存储在一个(称为“脚本”的)纯文本文件里以后运行。在下面的示例中,这两种模式都可以使用,但练习和复习题一般都要求用Python脚本来完成。
1.8.2 你好,Python
许多程序设计语言的介绍长久以来一直有一个传统,描述的第一个程序都是“Hello, World”。这个简单的程序输出一个名义上的问候,演示一种特定语言是如何产生结果以及如何表示文本的。在Python[1]中,这个程序的写法如下:
print('Hello, World!')
可以将这条语句键入Python的交互解释器里,也可以把它存为Python脚本后再执行。不管用哪一种方式,最后的结果都应该是:
Hello, World!
Python会把两个引号之间的文本返回给用户。
即使是在这种简单的Python脚本里面,也有几个需要注意的方面。首先,print是一个内置函数,是Python脚本用来产生输出的一个预定义操作,输出指的是一个能够让用户看到的程序结果。这个打印函数后面有一个开括号和一个闭括号,这两个括号之间的内容就是要打印的值。
其次,Python可以使用单引号来表示文本串。大写字母H前面的引号和感叹号后面的引号,分别表示由字符组成的字符串的开头和结尾,在Python中,这个字符串会被当作值。
程序设计语言能够非常精确地完成它们的指令。即使用户只是稍微修改一下打印语句中开始引号和结束引号之间的消息,最后打印出来的文本也会相应地变化。花一点儿时间,在打印语句中试试不同的大小写、不同的标点符号,甚至不同的单词,读者会看到确实是这样。
1.8.3 变量
Python允许用户给值命名以备日后使用,这是构造简洁、易懂的脚本时的一个重要抽象。这些命名的存储位置被称为变量(variable),类似于代数课程中的数学变量。下面来看一下略有增强的Hello World版本:
message = 'Hello, World!'
print(message)
在这个脚本中,第一行是一个赋值语句(assignment statement)。=号的使用可能会让习惯了等号的代数用法的初学者感到迷惑。这个赋值语句应该读作:“变量message被赋予字符串值'Hello, World!'。”通常,赋值语句由等号左边的变量名和等号右边的值构成。
Python是一种动态类型(dynamically typed)语言,这意味着,我们不需要在建立脚本时提前创建好一个名为message的变量,或是什么类型的值应该存储在message中,而只需要在这个脚本中说明我们的文本串会被赋给message,接着在后面的print语句中引用这个变量message就行了。
变量的命名很大程度上取决于Python用户。Python的简单规则是:变量名必须以字母开头,可以包含任意数量的字母、数字和下划线字符(_)。虽然对于一个两行的示例脚本来说,可能把变量命名为m就可以了,但有经验的程序员都会在他们的脚本中尽量给变量起一个有意义的、具有描述性的名字。
Python变量名是区分大小写的(case-sensitive),即有大小写问题。名为size的变量,与名为Size或SIZE的变量是截然不同的。有一小部分关键字(keyword),即为Python中的一些特殊含义保留的名字,不能用作变量名。在Python的内置帮助系统里能查看到这个关键字清单。
help('keywords')
变量可用于存储Python能表示的所有类型的值。
my_integer = 5
my_floating_point = 26.2
my_Boolean = True
my_string = 'characters'
观察上面这些值的类型,它们与本章前面介绍的表示方法是相对应的:布尔值真和假(见1.1节)、文本(见1.4节)、整数(见1.6节)和浮点数(见1.7节)。通过Python的其他代码(超出了本书简单介绍的范围),我们还能够用Python变量存储图像和声音数据(见1.4节)。
Python使用0x前缀来表示十六进制值,如
my_integer = 0xFF
print(my_integer)
不管程序员在推理过程中使用什么样的记数系统,指定一个十六进制的值都不会改变计算机存储器中该值的表示,存储器都会把整数值存储为许多个1和0。十六进制记数法仍然是人类在用的一种有助于理解脚本的快捷表示。因此,上面的print语句打印出来的是255,即十六进制0xFF的十进制解释,因为这是print的默认行为。用更复杂的print语句可以输出其他表示形式的值,但本书只讨论我们比较熟悉的十进制表示。
Unicode字符包含着无所不在的ASCII子集中所没有的字符,在文本编辑器支持Unicode字符时,可以直接在字符串里包含Unicode字符:
print('1000'] # Prints 1000, one thousand Indian Rupees
file:///D:\我的文档\Tencent Files\827161852\Image\C2C\PEYPY[0RMB60@R50R$JAR7S.png
或者用前缀'\u'和4个十六进制数字来指定Unicode字符:
print('\u00A31000') # Prints £1000, one thousand British
# Pounds Sterling
字符串的'\u00A3'部分会对英镑符号的Unicode表示进行编码。后面紧跟着写'1000',这样最终输出的货币符号和数量之间就不会有空格了:£1000。
除了Unicode文本串,这些示例语句引入了另外一种语言特性。#号表示注释(comment)的开始,这是一个人类可读的Python代码符号,在计算机执行时会被忽略。有经验的程序员会在他们的代码中使用注释来解释算法难懂的部分,包括历史或来源信息,或者只写些读代码的人应该注意的问题。#号右边一直到行尾的所有字符都会被Python忽略。
1.8.4 运算符和表达式
Python的内置运算符允许用各种熟悉的方式对值进行操作和组合。
print(3 + 4) # Prints "7", which is 3 plus 4.
print(5 - 6) # Prints "−1", which is 5 minus 6
print(7 * 8) # Prints "56", which is 7 times 8
print(45 / 4) # Prints "11.25", which is 45 divided by 4
print(2 ** 10) # Prints "1024", which is 2 to the 10th power
当一个操作(如45除以4)产生的是非整数结果(如11.25)时,Python会将表示类型隐式转换为浮点表示。如果希望结果是整数,就要使用另外一组运算符。
print(45 // 4) # Prints "11", which is 45 integer divided by 4
print(45 % 4) # Prints "1", because 4 * 11 + 1 = 45
双斜线(//)表示整除(integer floor division)运算符,百分号(%)表示取模(modulus)或取余运算符。将这两个计算综合起来,可读作:“4除45等于11,余数为1。”在前面的示例中,我们用**表示幂运算符,这看起来可能有些奇怪,因为在打印文本甚至一些其他程序设计语言中,幂运算符一般都是用插入符号(^)表示。在Python中,^运算符是一种按位布尔运算(bitwise Boolean operation)运算符,有关按位布尔运算的内容将在下一章中介绍。
还可以用一些直观的方式对字符串值进行组合和操作。
s = 'hello' + 'world'
t = s * 4
print(t) # Prints "helloworldhelloworldhelloworldhelloworld"
其中,加(+)运算符用于拼接(concatenate)字符串值,乘(*)运算符用于复制(replicate)字符串值。
有些内置运算符的多重含义会导致混淆。下面这个脚本会产出一个错误:
print('USD$' + 1000) # TypeError: Can't convert 'int' to str implicitly
这个错误指出,字符串拼接运算符不知道在第二个操作数不是字符串的情况下要怎么做。不过幸运的是,Python提供了允许将值从一种表示类型转换为另外一种表示类型的函数。int()函数能把浮点型值转换回整数表示,丢弃小数部分。如果字符串可以正确地拼出一个有效数字,那么int()函数还能将一个文本数字串转换为一个整数表示。同样地,str()函数能把数字表示转换成UTF-8编码的文本串。因此,对上述print语句做如下修改就能改正错误。
print('USD$' + str(1000)) # Prints "USD$1000"
1.8.5 货币转换
下面这个完整的Python脚本示例演示了许多本节要介绍的概念。给定一定数量的美元,脚本会对其进行货币转换,转换成4种其他货币。
# A converter for international currency exchange.
USD_to_GBP = 0.66 # Today's rate, US dollars to British Pounds
USD_to_EUR = 0.77 # Today's rate, US dollars to Euros
USD_to_JPY = 99.18 # Today's rate, US dollars to Japanese Yen
USD_to_INR = 59.52 # Today's rate, US dollars to Indian Rupees
GBP_sign = '\u00A3' # Unicode values for non-ASCII currency symbols.
EUR_sign = '\u20AC'
JPY_sign = '\u00A5'
INR_sign = '\u20B9'
dollars = 1000 # The number of dollars to convert
pounds = dollars * USD_to_GBP # Conversion calculations
euros = dollars * USD_to_EUR
yen = dollars * USD_to_JPY
rupees = dollars * USD_to_INR
print('Today, $' + str(dollars)) # Printing the results
print('converts to ' + GBP_sign + str(pounds))
print('converts to ' + EUR_sign + str(euros))
print('converts to ' + JPY_sign + str(yen))
print('converts to ' + INR_sign + str(rupees))
执行该脚本时,输出如下:
Today, $1000
converts to £660.0
converts to €770.0
converts to ¥99180.0
converts to file:///D:\我的文档\Tencent Files\827161852\Image\C2C\PEYPY[0RMB60@R50R$JAR7S.png159520.0
1.8.6 调试
程序设计语言对初学者不是很宽容,初学者在编写软件时,需要花费大量的时间努力查找代码中的bug或者错误。软件中的bug可分为3大类:语法错误(syntax error,键入时产生的符号错误)、语义错误(semantic error,程序含义的错误)和运行时错误(runtime error,程序运行时发生的错误)。
对于新手来说,语法错误是最常见的,包括一些简单的错误,如忘记文本串开头或者结尾的其中一个引号,没有关闭开括号,或者拼错函数名print。当Python解释器遇到这些错误时,通常会努力指出这些错误,显示问题代码所在行的行号以及问题描述。经过一些练习之后,初学者能很快学会识别和解释常见的错误情况。下面来看几个例子:
print(5 + )
SyntaxError: invalid syntax
上述表达式在加法运算符和闭括号之间缺少一个值。
print(5.e)
SyntaxError: invalid token
Python希望小数点后面是数字,而不是字母。
pront(5)
NameError: name 'pront' is not defined
就像叫错一个人的名字一样,拼错已知函数或变量的名字会产生混淆和尴尬。
语义错误是算法中的缺陷,或者某种语言表达算法方式中的缺陷。这样的例子可能包括:在一个计算中使用了错误的变量,或者弄错了复杂表达式中算术运算符的顺序。Python遵循运算符优先级的标准规则,所以在像total_pay = 40 + extra_hours pay_rate这样的表达式中,乘法会在加法之前执行,错误地计算总工资。(除非你的工资率恰好是1美元/小时。)使用括号正确地指定复杂表达式中的运算顺序,如total_pay = (40 + extra_hours) pay_rate,既可以避免语义错误,又可以避免写出的代码难于理解。 最后,这个层次上的运行时错误可能包括:无意识地除以0,或者使用未定义的变量。Python是从上到下读取语句的,在表达式里使用变量之前,Python必须看到变量的赋值语句。
测试是高效编写Python脚本(或者任何种类的实际程序)必不可少的一部分。在编写脚本时要频繁运行它,可能每完成一行代码就需要运行一次。这样做能尽早识别和修复语法错误,把程序设计者的注意力集中在脚本每一步应该做什么上。
问题与练习
1.是什么使Python成为一门解释型程序设计语言?
2.编写Python语句输出下列内容。
a. “Computer Science Rocks”这些词,后面跟一个感叹号。
b. 数字42。
c. π的近似值,精确到小数点后第4位。
3.编写Python语句按下列描述给变量赋值。
a. 将“programmer”这个词赋值给一个名为rockstar的变量。
b. 将1小时的秒数赋值给一个名为seconds_per_hour的变量。
c. 将人体的平均温度赋值给一个名为bodyTemp的变量。
4.编写一条Python语句,给定一个已有的名为bodyTemp的变量,单位为华氏度,将与其相等的摄氏度存储到一个名为metricBodyTemp的新变量中。