当前位置:首页 >> 建筑/土木 >>

Python入门教程


运行环境 1. 运行环境 要用 python 写程序,你必须先安装一个 python 的解释器。它可以存在于大多数平台(包括 Macintosh、Unix 和 Windows) 。更多与此有关的信息可以在 python 的网站上找到。你还应该 有一个文本编辑器(象 emacs、notepad 或者类似的东西) 。 编程是什么? 2. 编程是什么? 为计算机写程序其实就是给它一系列

的指令告诉它去做什么。计算机程序在某些方面就象是 菜谱,指导我们如何做菜的那种。例如[1] : 假日火腿沙拉 原料: 腌泡汁: 1/4 杯酸橙汁 1/4 杯低钠大豆酱油 1/4 杯水 1 大汤匙植物油 3/4 茶匙小茴香 1/2 茶匙牛至 1/4 茶匙热胡椒粉 2 片丁香、大蒜,捣碎 沙拉: 1 份(12 盎司)罐装少钠午餐肉火腿切成条状 1 个洋葱,切片 胡椒粉,切好的生菜 12 个樱桃西红柿,切半 方法: 把腌泡汁装在有合适盖子的广口瓶里摇匀。用塑料袋装上火腿,泼上腌泡汁,封住袋口。在 电冰箱里腌制 30 分钟。从塑料袋里取出火腿;准备 2 大汤匙腌泡汁,在煮锅里煮一下。加上 火腿、洋葱、绿色的胡椒。烧 3 到 4 分钟直到火腿熟了为止…… 当然, 没有一台计算机会懂这个……而且即便是懂, 大多数计算机也不可能烧制出一份沙拉。 那么, 我们该如何让这些变得对计算机来说更为友好一些呢?从根本上说依赖于两点: 首先, 我们必须以计算机可以理解的方式与之交流;其次还要和它谈论它能够做到的事情。 第一点意味着我们必须使用一种语言--一种已经为之准备好了解释器的程序设计语言,第

二点意味着我们不能期望计算机为我们做一份沙拉--但是我们可以让它做数字累加或者在 屏幕上打印东西之类的事情。 Hello…… 3. Hello…… 程序设计教程有一个传统,通常以在屏幕上打印“Hello, world!”这样的程序做为开始。对 python 来说,这非常简单:

print "Hello, world!"

它从根本上说很象上面的菜谱(尽管要短得多! 。它告诉计算机做什么:打印“Hello, ) world!” 。如果让它打印更多的废话该怎么做呢?很简单:

print "Hello, world!" print "Goodbye, world!"

不比上一个难,是不是?但是不怎么有趣……我们希望它可以处理更多的元素,就象沙拉菜 谱那样。那么,我们都有哪些元素呢?首先,有字符串,象“Hello, world!” ,除此之外还 有数字。假设我们打算让计算机为我们计算矩形的面积。我们可以给它如下的菜谱:

# The Area of a Rectangle # Ingredients: width = 20 height = 30 # Instructions: area = width * height print area

你大概可以看出它同火腿沙拉菜谱的相似性 (尽管有些细微的差别) 但它是如何工作的呢? 。 首先, 以#开始的行叫做注释事实上会被计算机忽略。 然而插入象这样小段的注释对于增强你 程序的可读性来说是很重要的。 接下来,看起来象 foo = bar 这样的行叫做赋值。对于 width = 20 这样的情况来说就是告

诉计算机从这里开始 width 就代表 20 了。它还意味着一个名字为“width”的变量从此被创 建了(如果它先前已经存在,那么会被重新覆盖) 。所以,我们以后使用这个变量的时候,计 算机就知道了它的值。因此, width * height 本质上同 20 * 30 一样会计算出 600 这个结果,然后赋给名称为“area”的变量。程序的最后一句在屏幕上打 印出变量“area”的值,所以你看到这个程序运行的最终结果仅仅是 600 注意: 在某些程序设计语言中, 你必须在程序开始的时候告诉计算机你将会用到哪些变量 (就 象沙拉中的元素)--而 python 足够聪明,所以你可以根据需要随时创建。 反馈 4. 反馈 现在,你可以执行一些简单,或者再复杂一点的计算了。比方说,你或许打算写一段程序来 计算圆形的面积而不是矩形的:

radius = 30 print radius * radius * 3.14

然而,这事实上并不比计算矩形面积的那个程序更有意思。至少在我看来是这样。它有些僵 硬。 如果我们看到半径为 31 的圆该怎么办?怎样让计算机知道?这有点象沙拉菜谱中的:烧 “ 3 到 4 分钟直到火腿熟了为止。 ”要知道何时烧熟,我们必须检查。我们需要反馈,或者提示。 计算机如何知道我们圆形的半径?同样需要输入资料……我们可以做的是告诉计算机半径是 多少:

radius = input("What is the radius?") print radius * radius * 3.14

现在程序变得漂亮一些了……input 是个被称为函数的东西。 (很快你将学习创建你自己的函

数。而 input 是 python 内建的函数。 )仅仅写下 input 什么也不会做……你必须在它的后面放上一对括号。所以 input()可以工作--它会简单的 要求用户输入半径的长度。而上面的那个版本对用户来说也许更友好一些,因为它首先打印 出了一个问题。当我们将诸如提问字符串“What is the radius?”之类的东西放在函数调用 的括号中时,这个过程被称为函数的参数传递。括号中的内容被称为参数。在上个例子中我 们传递了一个提问作为参数以便 input 知道在获得答案前应该先打印什么。 但是获得的答案如何到达 radius 变量呢?函数 input,调用时,会返回一个值(象许多其它 函数一样) 。你不一定非要使用这个值,但象我们这种情况,我们要使用它。这样,下面这两 个表达式有着很大的差别:

foo = input bar = input()

foo 现在包含 input 函数本身(所以它事实上可以象 foo("What is your age?")这样使用; 这被称为动态函数调用)而 bar 包含用户键入的值。

流程 5. 流程 现在我们可以编写程序执行简单的任务(运算和打印)并且可以获得用户输入了。这很有用, 但仍然局限在按顺序执行命令,也就是说--它们必须按照事先安排好的顺序执行。大多数 火腿沙拉菜谱是象这样顺序或者线性叙述的。但是如果我们打算让计算机检查沙拉是否烧好 该怎样告诉它呢?如果烧好了,那么应该从烘箱里把它取出来--否则的话,应该接着让它 烧更长一段时间什么的。我们如何表达这个? 我们想做的,其实是控制程序的流程。它可以从两个方向执行--要么拿开火腿,要不继续 让它留在烘箱里。我们可以选择,条件是它是否烧好。这被称为条件执行。我们可以这样写:

temperature = input("What is the temperature of the spam?") if temperature >; 50: print "The salad is properly cooked." else: print "Cook the salad some more."

意思很明显:如果温度超过 50(摄氏度) ,那么打印出信息告诉用户烧好了,否则,告诉用 户再烧制一段时间。 注意:缩进在 python 中很重要。条件执行(还有循环执行以及函数定义--见后面)中的语 句块必须被缩进(而且要缩进同等数量的空格;一个键相当于 8 个空格)以便解释器可以知 道它们从哪里开始到哪里结束。这同时也使程序变得更加可读。 让我们回到先前的面积计算问题。能看出来这段程序做什么吗?

# Area calculation program print "Welcome to the Area calculation program" print "---------------------------------------" print # Print out the menu: print "Please select a shape:" print "1 Rectangle" print "2 Circle" #Get the user's choice: shape = input(">; ") #Calculate the area: if shape == 1: height = input("Please enter the height: ") width = input("Please enter the width: ") area = height *width print "The area is ", area else: radius = input("Please enter the radius: ") area = 3.14 * (radius**2) print "The area is ", area

这个例子中的新东西: 1. 只使用 print 本身将打印出一个空行 2. ==检查两个值是否相等,与=不同,后者把表达式右侧的值赋给左侧的变量。这是一个非 常重要的差别! 3. **是 python 的幂运算符--因此半径的平方被写成 radius**2 4. print 能够打印出不止一个东西。只要用逗号把它们分开就可以了。 (它们在输出时会用

单个空格分开。 ) 这个程序很简单:它要一个数字,告诉它用户打算让它计算矩形或是圆形的面积。然后,使 用一个 if 语句(条件执行)来决定应当执行哪个语句块计算面积。这两个语句块同先前面积 计算例子中使用的语句块本质上是一样的。留意注释是如何使代码变得更加可读的。编程的 第一条戒律就是: “你应当注释! ”无论如何--它都是一个应该养成的好习惯。 练习 1: 扩展上面的程序使它包括正方形面积的计算,用户只要输入它一条边的长度就可以了。做这 个练习之前你需要了解一件事:如果你有两个以上的选择,你可以象这样写:

if foo == 1: # Do something... elif foo == 2: # Do something else... elif foo == 3: # If all else fails...

这里的 elif 是意思为“else if”的神秘代码:)。所以,如 foo 等于 1,做某件事;否则, 如果 foo 等于 2,那么做另外的一些事,等等。你也可以在程序中加入其它的选项--象三 角形以及任意多边形。随你的便。

循环 6. 循环 顺序执行和条件执行仅仅是程序设计三个基本语句块架构方式中的两个。第三个则是循环执 行。在上个段落中我假设了一种情况,检查火腿是否烧好,但很明显它并不适用。如果下次 检查时火腿仍然没烧好该怎么办?我们怎么知道需要检查多少次?事实上,我们不知道。而 且我们也没必要知道。我们可以要求计算机持续检查直到烧好了为止。怎么表达这个?你猜 到了--我们使用循环,或者说是重复执行。 python 有两种循环类型:while 循环和 for 循环。for 循环大概是最简单的。举个例子:

for food in "spam", "eggs", "tomatoes": print "I love", food

它的意思是:对于列表"spam", "eggs", "tomatoes"中的每个元素,都打印出你喜欢它。循 环中的语句块为每个元素执行一次,而且每次执行,当前的元素都被赋给变量 food(在这个 例子中) 。另外一个例子:

for number in range(1, 100): print "Hello, world!" print "Just", 100 - number, "more to go..." print "Hello, world" print "That was the last one... Phew!"

函数 range 返回给定范围的数字列表(包括第一个数字,不包括最后一个……这个例子中是 [1……99]。所以,这样解释它: ) 循环体为 1(包括)到 100(不包括)之间的数字每个执行一次。 (哪个是循环体以及随后的 表达式事实上做什么留下来做为练习。 ) 但这对我们的烧菜问题并没有实质的帮助。如果我们打算检查火腿一百次,那么这是个很好 的解决方案;但是我们不知道这是否够--或者太多了。我们只是希望它在温度达不到(或 者,直到它足够热--大致某个状态)的时候持续检查。所以,我们使用 while:

# Spam-cooking program # Fetch the function sleep from time import sleep print "Please start cooking the spam. (I'll be back in 3 minutes.)" # Wait for 3 minutes (that is, 3*60 seconds)... sleep(180) print "I'm baaack :)" # How hot is hot enough? hot_enough = 50 temperature = input("How hot is the spam?") while temperature < hot_enouth: print "Not hot enough... Cook it a bit more..." sleep(30) temperature = input("OK, How hot is it now?")

print "It's hot enough - You're done!"

这个例子中的新东西…… 1. 有些有用的函数被存储在模块中而且可以被导入。 此例中我们从 python 自带的 time 模块 中导入了函数 sleep(它休止给定的多少秒的时间)(做你自己的模块当然也是可能的……) 。 练习 2: 写一个程序,持续从用户获得数据然后相加,直到它们的和为 100。再写一个程序,从用户 那里获得 100 个数据,打印出它们的和。 Bigger Programs - Abstraction 如果想知道一本书的大致内容,你不会翻遍所有的页--你只是看看目录,是不是?它会列 出书的主要内容。现在--想像写一本菜谱。许多菜谱,像“奶油火腿通心面”和“瑞士火 腿馅饼”很可能包含相同的东西,比如火腿,在这种情况下--你肯定不会打算在每个菜谱 里都重复叙述如何制作火腿。 (好了……你事实上可能不做火腿……但是为了做例子, 请忍受 一下:)) 。你会把制作火腿的菜谱单独放在一个章节,而仅仅在其它章节里引用它。这样-- 代替在每个菜谱里都完整的描述,你只要引用章节的名称就可以了。在计算机编程中这被称 为抽象化。 我们是不是已经象这样运行了某些东西?是的。我们没有详细的告诉计算机如何从用户那里 获得一个答案 (好了--我们没有真的这样做……同样地……我们也没有真正的在做火腿:)) 而是简单的使用了 input--一个函数来代替。我们事实上可以构造我们自己的函数,来应 用于这种类型的抽象化中。 假设我们希望找到小于给定正数的最大整数。例如,给定 2.7,这个数应当是 2。这往往被称 为给定数的“底线(floor)。 ”(这事实上可以用 python 的内建函数 int 来处理,但是,请再 次忍受我拿它作例子……)我们该怎样做?一个简单的解决办法是从 0 开始试每一个可能的 数:

number = input("What is the number?") floor = 0 while floor <= number: floor = floor + 1 floor = floor - 1 print "The floor of ", number, "is ", floor

注意当 floor 不再小于(或者等于)给定数时循环结束了;我们加了太多 1 给它。因此我们 必须为它减去 1。如果我们希望把它应用于完整的数学运算该怎么办呢?我们不得不为求每 个数的基数("floor"-ing)而写一次完整的循环。这很不舒服……你可能猜到了我们代之以 什么:把它放在我们自己的函数中,命名为“floor” :

def floor(number): result = 0 while result <= number: result = result + 1 result = result - 1 return result

这个例子中的新东西…… 1. 函数用关键字 def 定义,函数名紧随其后并且要用括号把需要的参数括起来。 2. 如果要求函数返回一个值,要使用关键字 return 来处理(它同时也自动结束函数定义) 。 定义了函数之后,我们可以象这样使用它:

x = 2.7 y = floor(2.7)

执行后,y 的值应该是 2。定义拥有多个参数的函数也是可以的:

def sum(x, y): return x + y

练习 3 写一个函数,用欧几里德方法寻找两个数的一个共同因数。工作过程是这样的: 1. 2. 1. 2. 假设两个数,a 和 b,a 大于 b 重复以下步骤直到 b 变成 0: a 变为 b 的值 b 变成没有改变值之前的 a 除以没有改变值之前的 b 的余数

3. 返回 a 的最后一个值 提示: * 使用 a 和 b 作为函数的参数 * 简单的设定 a 大于 b * x 除以 z 的余数用表达式 x % z 来计算 * 两个变量可以象这样一起赋值:x, y = y, y+1。这里 x 被赋以值 y(这意味着,y 的值此 前已经指定)而且 y 被递增了 1。

深入函数 7. 深入函数 上面的练习怎么做?难吗?还不太清楚函数?别担心--我还没完成我的话题呢。 我们构建函数时使用的萃取方法称为过程抽象, 许多编程语言把关键字过程同函数一样使用。 事实上,这两个概念是不一样的,但是在 python 中它们都被称为函数(因为它们或多或少以 同样的方式定义和使用) 。 函数和过程(在其它语言中)的区别在哪里呢?嗯--就像你在前面的段落里看到的那样, 函数可以返回一个值。区别就是过程并不返回这样的值。许多时候,用这种方法把函数划分 为两种类型--返回值的和不返回值的--是很有用的。 不返回值的函数(过程)可以用作子程序或例行程序。我们调用这些函数,它们制造某些原 料,就象泡沫鲜奶之类的。我们可以在很多地方使用这个函数而不需要重写它的代码(这被 称为代码再利用--以后你还会知道,它意义不仅仅在这里) 。 这样的函数(或过程)的另一个有用性体现在--它改变了环境(例如,把糖和奶油混在一 起搅拌,它们的整个外部状态就变化了)让我们看个例子: def hello(who): print "Hello, ", who hello("world") # Prints out "Hello, world"

打印出内容是它一方面的作用,因为这是这个函数唯一需要做的事,它其实是一个典型的所 谓过程。但是……它事实上没有改变它的运行环境,是不是?它怎样才能改变呢?让我们试 一下: # The *wrong* way of doing it

age = 0 def setAge(a): age = a setAge(100) print age # Prints "0"

错在哪儿?错在函数 setAge 创建了它自己的也被命名为 age 的局部变量,它只在 setAge 函 数内部可用。那如何才可以避免出现这个问题呢?我们可以使用全局变量。 注意:全局变量在 python 中不常用。它们容易引起不好的代码组织结构,被称为意大利面代 码。 我这里使用它们是为了引出更复杂一点的技术问题--如果你可以请尽量避免使用它们。 第 8 章 通过告诉解释器一个变量是全局的(用象 global age 这样的表达式做) ,我们事实上 告诉了它在函数之外使用这个变量,而不是重新创建一个新的局部变量。 (所以,和局部 相反它是全局的。 )因此上面的程序可以象这样重写: # The correct, but not-so-good way of doing it age=0 def setAge(a): global age setAge(100) print age # Prints "100"

了解对象(随后谈到)后,你会发现更好的解决这个问题的办法是使用一个有 age 属 性和 setAge 方法的对象。在数据结构那段,你也将会发现一些函数改变它的环境的更好的 例子。 好了--那么真正的函数是什么样?什么是函数呢,事实上?数学函数象一种“机 器” ,获得输入然后计算结果。它会每次返回同样的结果,如果每次提供它同样的输入。 例如: def square(x): return x*x

这和数学上的函数 f(x)=x*x 一样。它的行为象一个精确的函数,仅仅依赖于它的输 入,在任何情况下都不改变它的环境。 所以--我这里描绘了两种构造函数的方法:一种类型更象是过程,不返回任何结 果;另一种更象是数学上的函数, (几乎)什么也不做就是为了返回一个结果。当然,在 这两种极端事物之间做某些事情是可能的,尽管当函数改变事物的时候,它应该清楚它改 变了。你可以通过标记它们的名字区分它们,例如为“纯粹”的函数使用象 square 这样的 名词而对类似过程那样的函数使用象 setAge 这样命令式的名字。 更多类型-数据结构 9. 更多类型-数据结构 现在--你已经知道了不少:怎样输入输出,怎样设计复杂的运算法则(程序)来执 行数学运算,但是好戏还在后头呢。 截止目前我们都在程序中使用了哪些成份呢?数字和字符串,对不对?没意思的种 类……现在让我们引入两三个其它的成份来让事情变得更有意思些。 数据结构是种组织数据的成份。 (惊奇,吃惊……)单个的数据没有什么真正的数据 结构,是不是?但是假设我们需要很多数放在一起做为一个成份--那就需要某种结构。 例如,我们可能想要一个数据列表。那很容易: [3, 6, 78, 93] 在循环那段我提到了列表,但没真正描述它。好--这里说的就是你如何创建它。只 需要列出元素,用逗号分开,再加上方括号就行了。 来看一个计算素数(只能被 1 和它本身整除的数)的例子: # Calculate all the primes below 1000 # (Not the best way to do it, but...) result = [1] candidates = range(3, 1000) base = 2 product = base while candidates: while product < 1000: if product in candidates: candidates.remove(product) product = product+base result.append(base)

base = candidates[0] product = base del candidates[0] result.append(base) print result

这个例子中的新东西…… 内建函数 range 事实上返回一个列表,可以象所有其它列表那样使用。 (它包括第 一个数,但是不包括最后一个数。 ) 列表可以当作逻辑变量使用。如果它非空,则为 true,否则为 false。因此,while candidates 意思是“while 名称为 candidates 的列表非空时”或者简单的说“while 存 在 candidates 时” 。 你可以用 if someElement in somelist 来检查一个元素是否在列表中。 你可以用 someList.remove(someElement)来删除 someList 中的 someElement。 你可以用 someList.append(something)为一个列表添加元素。事实上,你也可以使 用“+” (象 someList = someList+[something])。但是效率不是太高。 你可以通过在列表名之后加上用括号括起来的表示某元素位置的数字(很奇怪,列 表的第 1 个元素,位置是 0)来获得列表的某个元素。因此 someList[3]是 someList 列表的第四个元素(依次类推) 。 你可以使用关键字 del 删除变量。它也可以用来删除列表中的元素(就象这里) 。 因此 del someList[0]删除 someList 列表中的第一个元素。如果删除前列表是[1, 2, 3],删除后就变成了[2, 3]。 在继续叙述索引列表中的元素之前,我简单解释一下上面的例子。 这是古老算术的一个版本,称为“The Sieve of Erastothenes” (类似这样) 。它考量一 系列给定数字(在本例中是一个列表) ,然后有组织的删除已知不是素数的数字。如何知 道?只要看看它们是不是可以被分解为其它两个数就可以了。 我们从一个包含数字[2...999]的候选列表开始--我们知道 1 是素数(事实上,它可能 是也可能不是,看你问谁了) ,我们想得到小于 1000 的所有素数。 (事实上,我们的候 选列表是[3...999],但是 2 也是候选数字,因为它是我们的第一个 base) 。我们还有个叫 result 的列表,它任何时间都包含着最新的结果。最初的时候,它只包含 1。我们还有个叫 base 的变量。每次循环,我们删除是它的倍数的数字(它总是候选列表中最小的数) 。每次 循环之后,我们知道剩下的最小的数是素数(因为所有可以分解的数我们都删除了) 。

因此,我们把它加入 result,并把它设为新的 base,然后从列表里移除它(这样就不会对 它重复计算了) 。当候选列表为空时,result 列表将包含所有的素数。精巧吧,哈! 思考一下:第一次循环有什么特别吗?那时 base 是 2,但它一样经过了筛选。为什 么?为什么这不发生在其它的 base 值身上?我们打算移除 product 时能否确定它在候选列 表中呢?为什么? 接下来是什么呢?哦,是的……索引。还有切片。它们是从 python 列表中获得单个 元素的方法。你已经见到了普通的索引行为。它相当简单。事实上,我已经告诉了你所有 你需要知道的关于它的东西,除了一件事:负数索引从列表的末尾向前计算。所以, someList[-1]是 someList 的最后一个元素,someList[-2]是它之前的一个元素,依次类 推。 切片,仍然,对你来说是陌生的。它和索引相似,除了切片可以获得列表中的所有的 元素,而不仅仅是单个的元素。这如何做呢?象这样: food = [“spam”, “spam”, “eggs”, “sausages”, “spam”] print food[2:4] # Prints “['eggs', 'sausages']”

继续抽象-对象和面向对象编程 10. 继续抽象-对象和面向对象编程 现在有个比较热门的词叫做“面向对象编程” 。 就象本段标题暗示的那样,面向对象编程仅仅是另外一种抽象细节的方式。程序通过 命名将简单的描述抽象为复杂的操作。在面向对象编程时,我们不仅可以这样对待程序, 还可以把它们做为对象。 (现在,这肯定会让你吃惊,哈! )例如,如果编写烧火腿程 序,我们不用编写很多过程来处理温度、时间、成份等等,我们可以把它们结合为一个火 腿对象。或者,也许我们可以再有炉子对象和时钟对象……那么,象温度这类事物就变成 了火腿对象的一个属性,而时间可以从时钟对象读取。要使用我们的程序做某些事,我们 可以教给我们的对象某些方法;比如,炉子应当知道如何烹制火腿等。 那么--在 python 中我们如何做呢?我们不能直接制造一个对象。不能直接制造一个 炉子,而是做一个菜谱来描述炉子应该是什么样。这份菜谱因此就描述了一个被我们称为 炉子的一类对象。一个非常简单的炉子类可能是这样: class Oven: def insertSpam(self, spam): self.spam = spam def getSpam(self):

return self.spam

这看起来很难理解,还是怎样呢? 这个例子中的新东西…… 对象的类用关键字 class 定义。 类的名称通常以大写字母开始,而函数和变量(还有属性和方法)的名称以小写字 母开始。 方法(也就是让对象知道如何去做的函数和操作)的定义没有特别,但是要在类的 定义里面。 所有对象的方法应当有的第一个参数叫做 self(或者类似的……)原因很快就清楚 了。 对象的属性和方法可以这样来访问:mySpam.temperature = 2 或者 dilbert.be_nice ()。 我能猜到上面例子中的某些东西你仍然不清楚。例如,什么是 self?还有,现在我们 有了对象菜谱(也就是类) ,我们怎样事实上构造一个对象呢? 我们先颠倒一下顺序。对象通过象引用函数那样引用类名来创建: myOven = Oven()

myOven 包含了一个 Oven 对象,通常叫做 Oven 类的一个实例。假设我们也构造好了 一个 Spam 类,那么我们可象这样做: mySpam = Spam() myOven.insertSpam(mySpam)

myOven.spam 现在将包含 mySpam。怎么回事?因为,我们调用一个对象的某个方法 时,第一个参数,通常称为 self,总是包含对象本身。 (巧妙,哈! )这样,self.spam =spam 这一行设置当前 Oven 对象的 spam 属性的值为参数 spam。注意它们是两个不同的事物,尽管 在这个例子中它们都被称为 spam。 练习 3 答案 11. 练习 3 答案

这是这个运算法则的一个非常简洁的版本: def euclid(a, b): while b: a, b = b, a%b return a

-完-

PYTHON 教程 第一章介绍 脚本语言是类似 DOS 批处理、UNIX shell 程序的语言。脚本语言不需要每次编译再执行,并 且在执行中可以很容易地访问正在运行的程序,甚至可以动态地修改正在运行的程序,适用 于快速地开发以及完成一些简单的任务。在使用脚本语言时常常需要增的新的功能,但有时 因为脚本语言本来就已经很慢、很大、很复杂了而不能实现;或者,所需的功能涉及只能用 C 语言提供的系统调用或其他函数——通常所要解决的问题没有重要到必须用 C 语言重写的 程度;或者,解决问题需要诸如可变长度字符串等数据类型(如文件名的有序列表) ,这样的 数据类型在脚本语言中十分容易而 C 语言则需要很多工作才能实现;或者,编程者不熟悉 C 语言:这些情况下还是可以使用脚本语言的。 在这样的情况下,Python 可能正好适合你的需要。Python 使用简单,但它是一个真正的程序 语言,而且比 shell 提供了更多结构和对大型程序的支持。另一方面,它比 C 提供更多的错 误检查,它是一个非常高级的语言,内置了各种高级数据结构,如灵活的数组和字典,这些 数据结构要用 C 高效实现的话可能要花费你几天的时间。 由于 Python 具有更一般的数据结构, 它比 Awk 甚至 Perl 适用的范围都广, 而许多东西在 Python 内至少和在这些语言内一样容易。

Python 允许你把程序分解为模块,模块可以在其他 Python 程序中重用。它带有一大批标准 模块可以作为你自己的程序的基础——或作为学习 Python 编程的例子。 系统还提供了关于文 件输入输出、系统调用、插座(sockets)的东西,甚至提供了窗口系统(STDWIN)的通用接口。

Python 是一个解释性语言,因为不需要编译和连接所以能节省大量的程序开发时间。解释程 序可以交互使用,这样可以可以很容易地试验语言的各种特色,写只用一次的程序,或在从 底向上程序开发中测试函数。它也是一个方便的计算器。

Python 允许你写出非常严谨而且可读的程序。用 Python 写的程序通常都比相应的 C 程序要 短,因为如下几个理由:

高级的数据结构允许你用一个语句表达复杂的操作; 复合语句是靠缩进而不是用表示开始和结束的括号; 不需要变量声明或参量声明。 Python 是可扩充的:如果你会用 C 语言编程就很容易为解释程序增加新的内置函数或模块, 这样可以以最快速度执行关键操作, 或把 Python 程序和只能以二进制码提供的库 (如不同厂 商提供的图形库) 连接起来。 当你变得确实很在行时你可以把 Python 解释器与用 C 写的应用 相连接,把它作为该应用的扩展或命令语言。

Python 的命名是由 BBC 的“Monty Python's Flying Circus”节目而得,与蟒蛇没有什么关 系。 第二章解释程序的使用 在命令行键入 python 或在 Windows 环境下双击相应的图标可以进入 Python 的解释程序。 如果要运行储存在文件中 的 Python 程序,可以用 python 文件名

的形式。 进入解释程序的环境后,解释程序称为处于交互状态。在这种状态下系统用主提示提示输入 下一个命令,这一般是三个大于号(>>>),如果需要续行系统用次提示提示输入,缺省为三个 小数点(...)。在主提示下键入文件尾符号(在 UNIX 中为 Control-D,在 DOS 或 Windows 中 为 Control-Z)可以正常退出解释程序。 Python 解释程序的有些版本支持命令行编辑和命令历史,使用用 Emacs 或 vi 的键组合。

第三章基本使用 下面我们用例子来介绍 Python 的基本用法。在例子中,用户输入和系统输出靠有没有提示 (>>>和...)来分别。如果要试这些例子的话,需要键入提示后的所有命令,例子中没有提 示的行是系统的输出。 注意只有次提示的行意味着需要键入一个空行, 这用于结束多行命令。

3.1 用 Python 作计算器使用 启动解释程序,等待主提示>>>出现。解释程序可以作为计算器使用。键入一个表达式,解释

程序就可以输出结果。表达式的写法很直观:+,-,*,/, %, **等算符的作用和其它大多数 语言(如 Pascal 或 C)没什么差别,括号可以用来组合。例如:

>>>2+2 4 >>># 这 是 一 个 注 释 ... 2+24>>>2+2 # 和 代 码 在 同 一 行 的 注 释 4>>>(50-5*6)/45>>># 整数除法得下面的整数... 7/32>>>7/-3-3>>> 和 C 中一样,等于号用来给变量赋值,赋值的结果不显示: >>>width = 20>>>height = 5*9>>>width * height900>>> 可以同时给几个变量赋同一个值: >>>x = y = z = 0 # 把 x, y 和 z 赋零 >>>x0>>>y0>>>z0>>> Python 完全支持浮点数,混合类型的运算会把整数先转换成浮点数: >>>4 * 2.5 / 3.33.0303030303>>>7.0 / 23.5>>> Python 也提供了复数,方法是用 j 和 J 作为虚数单位,如 1+1j,3.14e-10j,等等。 3.2. 字符串 Python 除处理数字外还可以处理字符串,字符串用单撇号或双撇号包裹: >>>'spam eggs''spam eggs'>>>'doesn\'t'"doesn't">>>"doesn't""doesn't">>>'"Yes,"he said.''"Yes,"he said.'>>>"\"Yes,\"he said."'"Yes,"he said.'>>>'"Isn\'t,"she said.''"Isn\'t,"she said.'>>> 字符串输出格式与输入的样子相同, 都是用撇号包裹, 撇号和其它特殊字符用用反斜杠转义。 如果字符串中有单撇号而没有双撇号则用双撇号包裹,否则应该用单撇号包裹。后面要介绍 的 print 语句可以不带撇号或转义输出字符串。 字符串可以用+号连接起来,用*号重复: >>>word = 'Help' + 'A'>>>word'HelpA'>>>'<' '>''<HelpAHelpAHelpAHelpAHelpA>'>>> 字符串可以象在 C 中那样用下标索引,字符串的第一个字符下标为 0。 Python 没有单独的字符数据类型,一个字符就是长度为一的字符串。象在 Icon 语言中那样, 可以用片段(slice)记号来指定子串,片段即用冒号隔开的两个下标。 >>>word[4]'A'>>>word[0:2]'He'>>>word[2:4]'lp'>>> + word*5 +

片段有很好的缺省值:第一下标省略时缺省为零,第二下标省略时缺省为字符串的长度。 >>>word[:2] # 前两个字符'He'>>>word[2:] # 除前两个字符串外的部分'lpA'>>> 注意 s[:i] + s[i:] 等于 s 是片段运算的一个有用的恒等式。 >>>word[:2] + word[2:]'HelpA'>>>word[:3] + word[3:] 'HelpA'>>> 不合理的片段下标可以很好地得到解释:过大的下标被换成字符串长度,上界小于下界时返 回空串。 >>>word[1:100]'elpA'>>>word[10:]''>>>word[2:1]''>>> 下标允许为负数,这时从右向左数。例如: >>>word[-1] # 最后一个字符'A'>>>word[-2] # 倒数第二个字符'p'>>>word[-2:] # 最后两 个字符'pA'>>>word[:-2] # 除最后两个字符外的部分'Hel'>>> 但要注意的是 -0 实际还是 0,所以它不会从右向左数! >>>word[-0] # (因为 -0 等于 0)'H'>>> 超出范围的片段下标被截断,但在非片段的情况下不要这样: >>>word[-100:]'HelpA'>>>word[-10] # 错误 Traceback (innermost last):File"<stdin>", line 1IndexError: string index out of range >>> 记住片段意义的最好方法是把下标看成是字符之间的点, 第一个字符的左边界号码为 0。 n 有 个字符的字符串的最后一个字符的右边界下标为 n,例如: +---+---+---+---+---+| H | e | l | p | A |+---+---+---+---+---+0 1 2 3 4 5-5 -4 -3 -2 -1 第一行数字给出字符串中下标 0 到 5 的位置,第二行给出相应的负下标。从 i 到 j 的片段由 在边界 i 和 j 之间的字符组成。

对于非负下标, 如果下标都在界内, 则片段的长度为下标的差。 例如, word[1:3] 的长度为 2。 内置函数 len()返回字符串的长度: >>>s = 'supercalifragilisticexpialidocious'>>>len(s)34>>>

多行的长字符串也可以用行尾反斜杠续行,续行的行首空白不被忽略,如 hello ="This is a rather long string containing\n\several lines of text just as you would do in C.\n\Note that whitespace at the beginning of the line is\significant.\n"print hello 结果为

This is a rather long string containingseveral lines of text just as you would do in C.Note that whitespace at the beginning of the line is significant. 对于特别长的字符串(比如包含说明的几段文字) ,如果用上面的方式每行都用\n\结尾是很 麻烦的,特别是这样无法用象 Emacs 这样的功能强大的编辑器重新编排。对这种情况,可以 使用三重撇号,例如 hello =""" This string is bounded by triple double quotes (3 times").Unescaped newlines in the string are retained, though \it is still possible\nto use all normal escape sequences. Whitespace at the beginning of a line issignificant. If you need to include three opening quotesyou have to escape at least one of them, e.g. \""". This string ends in a newline.""" 三重撇号字符串也可以用三个单撇号,没有任何语义差别。 多行的字符串常量可以直接连接起来,字符串常量之间用空格分隔则在编译时可以自动连接 起来,这样可以把一个长字符串连接起来而不需要牺牲缩进对齐或性能,不象用加号连接需 要运算,也不象字符串串内的换行其行首空格需要保持。 3.3 列表 Python 中有几种复合数据类型,用来把其它值组合到一起。其中最灵活的是列表,可以写成 在方括号之间用逗号隔开的若干值(项) 。列表的项不必取同一类型。 >>>a = ['spam', 'eggs', 100, 1234]>>>a['spam', 'eggs', 100, 1234]>>> 象字符串下标那样,列表下标从 0 开始,列表可以取片段,可以连接,等等: >>>a[0]'spam'>>>a[3]1234>>>a[-2]100>>>a[1:-1]['eggs', 100]>>>a[:2] + ['bacon', 2*2]['spam', 'eggs', 'bacon', 4]>>>3*a[:3] + ['Boe!']['spam', 'eggs', 100, 'spam', 'eggs', 100, 'spam', 'eggs', 100, 'Boe!']>>> 与字符串不同的是列表是可变的,可以修改列表的每个元素: >>>a['spam', 'eggs', 100, 1234]>>>a[2] = a[2] + 23>>>a['spam', 'eggs', 123, 1234]>>>

也可以给一个片段重新赋值,这甚至可以改变表的大小: >>># 替换若干项:... a[0:2] = [1, 12]>>>a[1, 12, 123, 1234]>>># 去掉若干项:... a[0:2] = []>>>a[123, 1234]>>># 插 入 若 干 项 :... a[1:1] = ['bletch', 'xyzzy']>>>a[123, 'bletch', 'xyzzy', 1234]>>>a[:0] = a # 在开头插入自身>>>a[123, 'bletch', 'xyzzy', 1234, 123,'bletch', 'xyzzy', 1234]>>> 内置函数也使用于列表: >>>len(a)8>>> 可以建立嵌套列表(表的元素也是列表) ,如: >>>q = [2, 3]>>>p = [1, q, 4]>>>len(p)3>>>p[1][2, 3]>>>p[1][0]2>>>p[1].append('xtra') # 列表方法>>>p[1, [2, 3, 'xtra'], 4]>>>q[2, 3, 'xtra']>>> 注意这个例子中 p[1]和 q 实际是同一个对象! 也就是说它们只不过是同一个东西的两个名字 (引用)而已。 3.4 编程初步 Python 当然不是只能用来把两个数加到一起,它可以完成很复杂的工作。例如,我们可以写 出 Fibonacci 序列的开始几个: >>># Fibonacci 序列:... # 两个元素的和定义下一个... a, b = 0, 1>>>while b<10:... print b... a, b = b, a+b...112358>>> 这个例子介绍了几个新特色。 第一行包含一个多重赋值:变量 a 和 b 同时得到新值 0 和 1。在最后一行又用了多重赋值, 我们可以看出赋值时先把右边都算出后再进行赋值。 while 循环当循环条件(这里即: b<10)成立时不断执行。在 Python 中和 C 中一样,非零整数 值为真值,零为假值。条件也可以是字符串或列表或任何序列,长度为非零的为真,空序列 为假。例子中所用的是一个简单比较。标准的比较算符和 C 一样: <,>, ==,<=,>= 和 !=。 循环体是缩进的:缩进是 Python 用来组合语句的方式。Python 目前还不能提供智能自动缩 进,所以你需要自己为每个缩进行键入制表符或空格。实际使用中你可以用文本编辑程序为 Python 准备复杂的输入, 多数文本编辑程序都有自动缩进的功能。 在交互输入复合语句时必 修附加一个空行以指示复合语句的完成 (因为解释程序无法猜到哪是语句的最后一行)print 。 语句显示后面的表达式值。这和直接写出表达式不同,它可以显示多个表达式和字符串,而 且可以用于程序文件中。显示时字符串没有撇号,各项目之间插入一个空格,所以你可以以 精美的格式显示,如:

>>>i = 256*256>>>print 'The value of i is', iThe value of i is 65536>>> 在尾部写一个逗号可以避免最后换行: >>>a, b = 0, 1>>>while b<1000:... print b,... a, b = b, a+b...1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987>>> 注意如果前一行没有结束的话系统在显示提示之前先换行。 Python 还提供了和 C 语言一样的 printf 格式的输出方式, 这是用%实现的, 左边是格式, 如:

>>>print 'The value of 1/7 is approximately %5.3f.' % 0.142857The value of 1/7 is approximately 0.143.>>> 如果有多个需要输出的项百分号右边的项可以是一个序组,如 >>>print"Name: %-10s Age: %3d"% ("White", 31)Name: White Age: 31 第四章流程控制 前面我们已经见到了如何由用 while 结构控制流程运行。这一章我们介绍更多的控制结构。 Python 具有和其它语言类似的控制结构但略有差别。 4.1 If 语句 If 语句可能是最基本的程序分支语句了。例如: >>>if x<0:... x = 0... print 'Negative changed to zero'... elif x == 0:... print 'Zero'... elif x == 1:... print 'Single'... else:... print 'More'... 可以有零到多个 elif 部分,else 部分可选。关键字 elif 是 else if 的缩写,这样可以缩短 语句行长度。其它语言中 switch 或 case 语句可以用 if...elif...elif...语句组来实现。 4.2 for 语句 Python 中的 for 语句与你可能熟悉的 C 或者 Pascal 中的相应语句略有不同。 不象 Pascal 那 样总是对数字序列进行循环, 也不是象 C 中那样完全由程序员自由地控制循环条件和循环体, Python 的 for 循环是对任意种类的序列(如列表或字符串)按出现次序遍历每一项。例如: >>># 计算字符串长:... a = ['cat', 'window', 'defenestrate']>>>for x in a:... print x, len(x)...cat 3window 6defenestrate 12>>> 尽量不要在循环体内修改用来控制循环的序列(当然,只有可变的序列类型如列表才有可能 被修改) ,这样程序可能会出问题。如果需要这样,比如说要复制某些项,可以用序列的副本 来控制循环。片段记号让你很容易生成副本: >>>for x in a[:]: # 生 成 整 个 列 表 的 片 段 副 本 ... if len(x)>6: a.insert(0, x)...>>>a['defenestrate', 'cat', 'window', 'defenestrate']>>>

结果是把列表中长度超过 6 个字符的字符串插入到列表开头。 4.3 range() 函数 如果确实需要对一列数字进行循环的话,可以使用内置函数 range()。它生成包含数字序列 的列表,如: >>>range(10)[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>>> 注意给出的终点永远不出现在生成的列表中, range(10)生成一个十个数的列表, 恰好是长度 为 10 的序列的合法下标的各个值。也可以指定不同的起始点,或者指定不同的间隔(甚至负 数) : >>>range(5, 10)[5, 6, 7, 8, 9]>>>range(0, 10, 3)[0, 3, 6, 9]>>>range(-10, -100, -30)[-10, -40, -70]>>> 为了对序列的下标进行循环,如下联合使用 range() 和 len(): >>>a = ['Mary', 'had', 'a', 'little', 'lamb']>>>for i in range(len(a)):... print i, a[i]...0 Mary1 had2 a3 little4 lamb>>> 4.4 break 语句,continue 语句和循环中的 else 子句 如同 C 语言一样, break 语句跳出其所处的最内层的 for 或 while 循环, continue 语句继续 下一循环步。 循环语句还可以带一个 else 子句,当循环正常结束时执行其内容,但如果循环是用 break 语句跳出的则不执行其内容。下例说明了这种用法,此例求素数: >>>for n in range(2, 10):... for x in range(2, n):... if n % x == 0:... print n, 'equals', x, '*', n/x... break... else:... print n, 'is a prime number'...2 is a prime number3 is a prime number4 equals 2 * 25 is a prime number6 equals 2 * 37 is a prime number8 equals 2 * 49 equals 3 * 3>>> 4.5 pass 语句 pass 语句不执行任何操作, 当语法要求一个语句而程序不需要执行操作时就用此语句。 例如: >>>while 1:... pass # 等待键盘中断... 4.6 函数定义 我们可以定义一个函数用来计算某一界限以下的所有 Fibonacci 序列值: >>>def fib(n): # 写出 n 以下的所有 Fibonacci 序列值... a, b = 0, 1... while b<n:... print b,... a, b = b, a+b...>>># 调用刚刚定义的函数:... fib(2000) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597>>> 其中关键字 def 开始一个函数定义,其后应该是函数名,括号内的形参表,以冒号结束。构 成函数体的各语句从下一行开始, 用一个制表符缩进。 函数的第一个语句可以是一个字符串, 如果是的话,这个字符串就是函数的文档字符串,简称为 docstring。有一些工具可以利用 文档字符串自动生成可打印的文档,或者让用户交互地浏览代码,所以在自己编程时加入文

档字符串是一个好习惯,应该养成这样的习惯。 函数在执行时对局部变量引入一个新的符号表。函数中的变量赋值都存入局部符号表;引用 变量时变量名先从局部符号表中查找, 然后在全局符号表中查找, 最后从内置的名字中查找。 因此,在函数中不能直接对全局变量赋值(除非用了 global 语句来说明) ,但可以引用全局 变量的值。 函数调用的实参被引入函数的局部符号表,即函数的参数是按值调用的。函数再调用其它函 数时为该函数生成一个新的符号表。但是严格地说,函数的调用是按引用调用的,因为如果 参数是一个可变类型如列表的话在函数中改变形参的内容将导致实参的内容被改变(不改变 的是实参名字的绑定关系) 。 函数定义把函数名放入当前符号表。函数名的值类型为用户自定义函数,这个值可以赋给另 一个名字,从而这个名字也代表相同的函数。这可以作为一般的改名方法: >>>fib<function object at 10042ed0>>>>f = fib>>>f(100)1 1 2 3 5 8 13 21 34 55 89>>> 你可能会说 fib 不是函数而是过程。Python 和 C 一样,过程只是不返回值的函数。实际上, 严格地说,过程也返回一个值,只不过是一个很没意思的值。这个值叫做 None(这是一个内 置的名字) 解释程序交互运行时如果只需要显示这个值的话就会忽略不显示。 。 如果希望显示 的话可以用 print 语句: >>>print fib(0)None>>> 也可以写一个函数返回 Fibonacci 序列的数值列表而不是显示 这些值: >>>def fib2(n): # 返回直到 n 的 Fibonacci 序列值... result = []... a, b = 0, 1... while b<n:... result.append(b) # 解释见下面... a, b = b, a+b... return result...>>>f100 = fib2(100) # 调用>>>f100 # 输出结果[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>> 这个例子也演示了新的 Python 特色:return 语句从函数中退出并返回一个值。不带返回值 的 return 可以从过程中间退出,运行到过程的末尾也可以退出,这两种情况下返回 None。 语句 result.append(b)调用列表对象 result 的一个方法。方法是“属于”一个对象的函数, 引用格式为 obj.methodname, 其中 obj 是某个对象 (也允许是一个表达式) methodname 是 , 由该对象的类型定义的一个方法的名字。不同的不同的方法。不同类型的方法可以使用相同 的名字而不致引起误解。 (可以定义自己的对象类型和方法,使用类,本文后面会讨论这个话 题) 。例子中的 append()方法时列表对象的方法,它在列表末尾增加一个新元素。在本例中 这等价于“result = result + [b]” ,只是更有效。 4.7 函数参数 可以定义使用可变个数参数的函数。这样的定义方法有三种,可以联合使用。 4.7.1 参数缺省值 可以为一个参数或几个参数指定缺省值。 这样定义的函数在调用时实参个数可以比定义时少。 例如: def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):while 1:ok =

raw_input(prompt)if ok in ('y', 'ye', 'yes'): return 1if ok in ('n', 'no', 'nop', 'nope'): return 0retries = retries - 1if retries<0: raise IOError, 'refusenik user'print complaint 这个函数在调用时既可以这样调用:ask_ok('Do you really want to quit?'),或者可以这 样调用:ask_ok('OK to overwrite the file?', 2)。缺省值是在函数定义时的定义作用域 中计算的,所以例如: i = 5def f(arg = i): print argi = 6f() 将显示 5。 注意:缺省值只计算一次。当缺省值是可变对象如列表或字典时这一点是要注意的。例如, 以下函数会在以后的调用中累加它的值: def f(a, l = []):l.append(a)return lprint f(1)print f(2)print f(3)This will print [1][1, 2][1, 2, 3] 如果你不希望缺省值在连续的调用中被保留,可以象下面这样改写函数: def f(a, l = None):if l is None:l = []l.append(a)return l 4.7.2 关键字参数 函数调用时也可以象 “关键字 = 值” 这样指定实参, 其中关键字是定义时使用的形参的名字。 例如: def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):print"-This parrot wouldn't", action,print"if you put", voltage,"Volts through it."print"-Lovely plumage, the", typeprint"-- It's", state,"!" 可以用如下几种方式调用: parrot(1000) # 缺省值 parrot(action = 'VOOOOOM', voltage = 1000000) # 关键字,缺 省值,次序可变 parrot('a thousand', state = 'pushing up the daisies') # 位置参数, 缺省值,关键字 parrot('a million', 'bereft of life', 'jump') # 位置参数,缺省值 但以下几种调用方式是错误的:

parrot() # 非缺省的参数没有提供 parrot(voltage=5.0, 'dead') # 关键字参数后面又出 现了非关键字参数 parrot(110, voltage=220) # 参数值重复提供 parrot(actor='John Cleese') # 未知关键字 一般说来,实参表中位置参数在前,关键字参数在后,关键字名字必须是形参名字。形参有

没有缺省值都可以用关键字参数的形式调用。每一形参至多只能对应一个实参,因此,已经 由位置参数传入值的形参就不能在同一调用中再作为关键字参数。 如果形参表中有一个形为**name 的形参,在调用时这个形参可以接收一个字典,字典中包含 所有不与任何形参匹配的关键字参数。形参表中还可以使用一个特殊的如*name 的形参,它 将接受所有不能匹配的位置参数组成的一个序表。*name 只能在**name 之前出现。例如,如 果定义了下面的函数: def cheeseshop(kind, *arguments, **keywords):print"-- Do you have any", kind, '?'print"-- I'm sorry, we're all out of", kindfor arg in arguments: print argprint '-'*40for kw in keywords.keys(): print kw, ':', keywords[kw] 就可以象下面这样调用: cheeseshop('Limburger',"It's very runny, sir.","It's really very, VERY runny, sir.",client='John Cleese',shopkeeper='Michael Palin',sketch='Cheese Shop Sketch') 结果显示: -- Do you have any Limburger ?-- I'm sorry, we're all out of LimburgerIt's very runny, sir.It's really very, VERY runny, sir.----------------------------------------client : John Cleeseshopkeeper : Michael Palinsketch : Cheese Shop Sketch 4.7.3 任意个数参数 在所有有名的形参的后面可以有两个特殊的形参,一个以*args 的形式命名,一个以**kw 的 形式命名。有了*args 形式的形参后函数在调用时就可以在正常的能匹配的实参表后面输入 任意个数的参数, 这些参数组成一个序表赋给 args 形参, 不能匹配的关键字参数组成一个字 典赋给 kw 形参。在任意个数形参之前可以有 0 到多个正常的参数。例如: def fprintf(file, format, *args):file.write(format % args) 4.7.4 Lambda 形式 因为许多人的要求,Python 中加入了一些在函数编程语言和 Lisp 中常见的功能。可以用 lambda 关键字来定义小的无名函数。这是一个返回其两个参数的和的函数: “lambda a, b: a+b” 。Lambda 形式可以用于任何需要函数对象的地方。从句法上讲 lambda 形式局限于一个 表达式。从语义上讲,这只是正常的函数定义的句法甜食。像嵌套函数定义一样,lambda 形 式不能访问包含其定义的作用域中的变量,但审慎地使用缺省参数之可以绕过这个限制。例 如: def make_incrementor(n):return lambda x, incr=n: x+incr 4.7.5 文档字符串 关于文档字符串的内容与格式正在形成一些惯例。第一行应该为简短的对象目的概括说明。 为了简明起见,这一行不应该提及对象的名字或类型,因为这些可以通过其他途径得知(当 然如果对象名字就是一个描述函数操作的动词则当然可以提及其名字)着以行应该用大些字 。 母开始,以句点结尾。如果文档字符串中有多行,第二行应该是空行,把概括说明与其它说

明分开。以下的行可以是一段或几段,描述对象的调用方法,它的副作用,等等。 Python 的扫描程序不会从多行字符串中去掉缩进空白, 所以处理文档的工具需要自己处理缩 进。只要遵循如下的惯例就可以有利于缩进空白的处理。在第一行之后的第一个非空白的行 决定整个文档字符串的缩进数量(我们不用第一行,因为它经常是直接跟在表示字符串开始 的引号后面) 文档字符串中除第一行以外的各行都要删除等价于此行的缩进量的空白。 。 对制 表符将扩展为空格后再删除。

第五章 Python 数据结构 本章更详细地讨论一些已经讲过的数据类型的使用,并引入一些新的类型。 5.1 列表 列表数据类型还有其它一些方法。下面是列表对象的所有方法:

insert(i, x) ---- 在指定位置插入一项。第一自变量是要在哪一个元素前面插入,用下标 表示。例如,a.insert(0, x)在列表前面插入,a.insert(len(a), x)等价于 a.append(x) 。 append(x) ---- 等价于 a.insert(len(a), x) index(x) ---- 在列表中查找值 x 然后返回第一个值为 x 的元素的下标。没有找到时出错。 remove(x) ---- 从列表中删去第一个值为 x 的元素,找不到时出错。 sort() ---- 对列表元素在原位排序。注意这个方法改变列表,而不是返回排序后的列表。 reverse() ---- 把列表元素反序。改变列表。 count(x) ---- 返回 x 在列表中出现的次数。

下例使用了所有的列表方法: >>>a = [66.6, 333, 333, 1, 1234.5]>>>print a.count(333), a.count(66.6), a.count('x')2 1 0>>>a.insert(2, -1)>>>a.append(333)>>>a[66.6, 333, -1, 333, 1, 1234.5, 333]>>>a.index(333)1>>>a.remove(333)>>>a[66.6, -1, 333, 1, 1234.5, 333]>>>a.reverse()>>>a[333, 1234.5, 1, 333, -1, 66.6]>>>a.sort()>>>a[-1, 1, 66.6, 333, 333, 1234.5] 5.1.1 函数程序设计工具 Python 中有一些函数程序设计风格的东西,例如前面我们看到的 lambda 形式。关于列表有 三个非常有用的内置函数:filter(), map()和 reduce()。

“filter(函数, 序列)”返回一个序列(尽可能与原来同类型) ,序列元素是原序列中由指定 的函数筛选出来的那些,筛选规则是“函数(序列元素)=true” 。filter()可以用来取出满足 条件的子集。例如,为了计算一些素数: >>>def f(x): return x % 2 != 0 and x % 3 != 0...>>>filter(f, range(2, 25))[5, 7, 11, 13, 17, 19, 23] “map(函数, 序列)” 对指定序列的每一项调用指定的函数, 结果为返回值组成的列表。 map() 可以对序列进行隐式循环。例如,要计算三次方,可用: >>>def cube(x): return x*x*x...>>>map(cube, range(1, 11))[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000] 可以有多个序列作为自变量,这时指定的函数必须也有相同个数的自变量,函数从每个序列 分别取出对应元素作为自变量进行调用(如果某个序列比其它的短则取出的值是 None) 。如 果指定的函数是 None,map()把它当成一个返回自己的自变量的恒同函数。在函数用 None 的 情况下指定多个序列可以把多个序列搭配起来,比如“map(None, list1, list2)”可以把两 个列表组合为一个成对值的列表。见下例: >>>seq = range(8)>>>def square(x): return x*x...>>>map(None, seq, map(square, seq))[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)] “reduce(函数, 序列)”用来进行类似累加这样的操作,这里的函数是一个两个子变量的函 数,reduce()先对序列的前两项调用函数得到一个结果,然后对结果和序列下一项调用函数 得到一个新结果,如此进行到序列尾部。例如,要计算 1 到 10 的和: >>>def add(x,y): return x+y...>>>reduce(add, range(1, 11))55 如果序列中只有一个值则返回这个值,序列为空时会产生例外。可以指定第三个自变量作为 初始值。有初始值时对空序列函数将返回初始值,否则函数先对初始值和序列第一项作用, 然后对结果和序列下一项作用,如此进行到序列尾。例如: >>>def sum(seq):... def add(x,y): return x+y... return reduce(add, seq, 0)...>>>sum(range(1, 11))55>>>sum([])0 5.2 del 语句 上面我们看到, 列表的 remove()方法可以从列表中删去某个取值的项, 我们还可以用 del 语 句来删除指定下标的项。也可以用 del 语句从列表中删除一个片断(前面我们是用给片断赋 空列表的办法删除片断的) 。例如: >>>a[-1, 1, 66.6, 333, 333, 1234.5]>>>del a[0]>>>a[1, 66.6, 333, 333, 1234.5]>>>del a[2:4]>>>a[1, 66.6, 1234.5] del 也可以用来删除整个变量,例如:

>>>del a 变量删除以后再引用该变量就会出错 (除非又给它赋值了) 后面我们还会看到 del 的其它一 。 些应用。 5.3 序表和序列 我们看到列表和字符串有许多共同点,例如,下标和片断运算。它们都属于序列数据类型。 因为 Python 是一个正在不断发展的语言, 以后还可能会加入其它的序列数据类型。 现在还有 一种标准的序列数据类型,称为序表(tuple) 。 序表由一系列值用逗号分隔而成,例如:

>>>t = 12345, 54321, 'hello!'>>>t[0]12345>>>t(12345, 54321, 'hello!')>>># 序表允 许嵌套:... u = t, (1, 2, 3, 4, 5)>>>u((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))

输出的序表总是用括号包围,这样可以保证嵌套序表得以正确解释。输入时可以有括号也可 以没有括号,当经常是必须有括号(如果序表是一个大表达式的一部分) 。 序表有许多用处,例如, (x,y)坐标对,数据库中的职工纪录,等等。序表与字符串一样是 不可变的:不允许对序表的某一项赋值。 生成序表时对 0 项或 1 项的序表有特殊的规定:空序表用一对空括号表示;只有一项的序表 用一个之后面跟一个抖好表示(指把这个值放在括号内是不够的) 。这样写不够美观,但很有 效。例如: >>>empty = ()>>>singleton = 'hello', #<-comma>>>len(empty)0>>>len(singleton)1>>>singleton('hello',) note trailing

语句 t = 12345, 54321, 'hello!'是序表打包的一个实例:12345, 54321 和'hello!'这些值 被打包进了一个序表中。相反的操作也是允许的,例如: >>>x, y, z = t 这叫做序表解包。序表解包要求等号左边的变量个数等于序表的长度。注意多重赋值只是序 表打包和序表解包的联合使用。有时也对列表进行类似操作,即列表解包。只要把各变量写 成一个列表就可以进行解包: >>>a = ['spam', 'eggs', 100, 1234]>>>[a1, a2, a3, a4] = a 5.4 字典 Python 内置的另一个有用的数据类型是字典。字典在其它语言中有时被称为“关联记忆”或 “关联数组” 。字典不象序列,它不是用在一个范围之内的数字下标来索引,而是用键值来索 引,键值可以是任何不可变类型。字符串和数值总可以作键值。如果序表只包含字符串、数 值或序表则序表也可以作键值使用。列表不能用作键值,因为列表可以用其 append()方法就

地改变值。 最好把字典看成是一系列未排序的“键值:值”的集合,在同一字典内键值是互不相同的。 一对空大括号产生一个空字典:{}。在大括号内加入用逗号分开的“键值:值”对可以在字 典内加入初始的键值和值对,字典在输出时也是这样显示的。对字典的主要操作是以某个键 值保存一个值,以及给定键值后查找对应的值。也可以用 del 删除某个键值:值对。如果用 一个已有定义的键值保存某个值则原来的植被遗忘。用不存在的键值去查找会出错。 字典对象的 keys()方法返回字典中所有键值组成的列表,次序是随机的。需要排序时只要对 返回的键值列表使用 sort()方法。为了检查某个键值是否在字典中,使用字典的 has_key() 方法。 下面是字典使用的一个简单例子: >>>tel = {'jack': 4098, 'sape': 4139}>>>tel['guido'] = 4127>>>tel{'sape': 4139, 'guido': 4127, 'jack': 4098}>>>tel['jack']4098>>>del tel['sape']>>>tel['irv'] = 4127>>>tel{'guido': 4127, 'irv': 4127, 'jack': 4098}>>>tel.keys()['guido', 'irv', 'jack']>>>tel.has_key('guido')1 5.5 条件的进一步讨论 在 while 语句和 if 语句中使用的条件除了可以使用比较之外还可以包含其它的运算符。 比较 运算符“in”和“not in”可以检查一个值是否在一个序列中。运算符“is”和“is not ” 比较两个对象是否恰好是同一个对象,这只对象列表这样的可变对象有意义。所有比较运算 优先级相同,而比较运算的优先级比所有数值运算优先级低。 比较允许连写,例如,a<b == c 检查是否 a 小于等于 b 而且 b 等于 c。 比较可以用逻辑运算符 and 和 or 连接起来,比较的结果(或其它任何逻辑表达式)可以用 not 取反。逻辑运算符又比所有比较运算符低,在逻辑运算符中,not 优先级最高,or 的优 先级最低,所以“A and not B or C”应解释为“(A and (not B)) or C” 。当然,可以用括 号来表示所需的组合条件。 逻辑运算符 and 和 or 称为“短路”运算符:运算符两侧的表达式是先计算左边的,如果左边 的结果已知则整体结果已知就不再计算右边的表达式。例如,如果 A 和 C 为真而 B 为假则“A and B and C”不会计算表达式 C。一般地,当短路运算符的运算结果不是用作逻辑值的时候 返回的是最后求值的那个表达式的值。 可以把比较或其它逻辑表达式的结果赋给一个变量。例如: >>>string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'>>>non_null = string1 or string2 or string3>>>non_null'Trondheim' 注意 Python 和 C 不同,表达式中不能进行赋值。 5.6 序列与其它类型的比较 序列对象可以和其它同序列类型的对象比较。比较使用字典序:先比较最前面两项,如果这

两项不同则结果可以确定;如果这两项相同,就比较下面的两项,如此下去,直到有一个序 列到头为止。如果某两项本身也是同类型的序列,则进行递归的字典序比较。如果两个序列 的所有各项都相等,则这两个序列相等。如果一个序列是另一个序列的一个初始子序列,短 的一个是较小的一个。字符串的字典序比较按各个字符的 ASCII 次序进行。下面是一些序列 比较的实例: (1, 2, 3)<(1, 2, 4)[1, 2, 3]<[1, 2, 4]'ABC'<'C'<'Pascal'<'Python'(1, 2, 3, 4)<(1, 2, 4)(1, 2)<(1, 2, -1)(1, 2, 3) = (1.0, 2.0, 3.0)(1, 2, ('aa', 'ab'))<(1, 2, ('abc', 'a'), 4) 注意不同类型的对象比较目前也是合法的。结果是确定的但却没有什么意义:不同类型是按 类型的名字排序的。所以,列表(list)总是小于字符串(string) ,字符串总是小于序表 (tuple) ,等等。但是程序中不能依赖这样的比较规则,语言实现可能会改变。不同的数值 类型可以按数值来比较,所以 0 等于 0.0,等等。

第六章模块 如果退出 Python 解释程序然后再进入,原有的定义(函数和变量)就丢失了。所以,如果需 要写长一点的程序,最好用一个文本编辑程序为解释程序准备输入,然后以程序文件作为输 入来运行 Python 解释程序,这称为准备脚本(script) 。当你的程序变长时,最好把它拆分 成几个文件以利于维护。你还可能想在几个程序中都使用某个很方便的函数,但又不想把函 数定义赋值到每一个程序中。 为了支持这些, Python 有一种办法可以把定义放在一个文件中然后就可以在一个脚本中或交 互运行中调用。这样的文件叫做一个模块;模块中的定义可以导入其它模块或主模块(主模 块指在解释程序顶级执行的脚本或交互执行的程序所能访问的变量集合) 。 模块是包含了 Python 定义和语句的文件。文件名由模块名加上后缀“.py”构成。在模块内, 模块的名字(作为一个字符串)可以由全局变量__name__的值获知。例如,在 Python 的搜索 路径中用你习惯使用的文本编辑器(Python 1.5.2 包含了一个用 Tkinter 编写的 IDLE 集成 开发环境,MS Windows 下有一个 PythonWin 界面也可以进行 Python 程序编辑)生成一个名 为“fibo.py ”的文件,包含如下内容: # Fibonacci numbers module def fib(n): # 输出小于 n 的 Fibonacci 序列 a, b = 0, 1while b<n:print b,a, b = b, a+b def fib2(n): # 返回小于 n 的 Fibonacci 序列 result = []a, b = 0, 1while b<n:result.append(b)a, b = b, a+breturn result 然后进入 Python 解释程序 (在 IDLE 或 PythonWin 中可以直接进入解释程序窗口) 用如下命 , 令可以导入模块: >>>import fibo

这不会把模块 fibo 中的函数的名字直接引入当前的符号表,这只是把模块名 fibo 引入。可 以用模块名来访问其中的函数: >>>fibo.fib(1000)1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987>>>fibo.fib2(100)[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]>>>fibo.__name__'fibo' 如果经常使用某个函数可以给它赋一个局部名字: >>>fib = fibo.fib>>>fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377 6.1 模块的进一步介绍 模块除了可以包含函数定义之外也可以包含可执行语句。这些可执行语句用来初始化模块, 它们只在模块第一次被导入时执行。 每个模块有自己私有的符号表,这个私有符号表对于模块中的所有函数而言却是它们的全局 符号表。 因此, 模块作者可以在模块中使用全局变量而不需担心与模块用户的全局变量冲突。 另一方面, 如果你有把握的话也可以用访问模块中函数的格式, modname.itemname 的方法 即 来修改模块中的全局变量。 模块可以导入其它模块。我们通常把所有的导入语句放在模块(或脚本)的开始位置,这不 是规定要求的。导入的模块名放入模块的全局符号表中。 导入还有另一种用法,可以把模块中的名字直接导入使用者的符号表。例如: >>>from fibo import fib, fib2>>>fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377 这不会把模块名导入使用者的符号表中(例如,上面例子中 fibo 就没有定义) 。 还有一种办法可以导入一个模块中定义的所有名字: >>>from fibo import *>>>fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

这可以把模块中除了以下划线结尾的所有名字导入。 6.1.1 模块搜索路径 在导入名为 spam 的模块时,解释程序先在当前目录中寻找名为“spam.py”的文件,然后从 环境变量 PYTHONPATH 所定义的目录列表中寻找。PYTHONPATH 的用法和可执行文件的搜索路 径 PATH 用法相同,都是一个目录列表。当 PYTHONPATH 未设置的时候,或者文件仍找不到, 则 搜 索 继 续 在 安 装 时 设 定 的 缺 省 路 径 搜 索 , 在 Unix 中 , 这 通 常 是 “.:/usr/local/lib/python” 。 实际上, 模块是按变量 sys.path 指定的路径搜索的, 此变量在解释程序启动时初始化为包含 输入脚本的目录(或当前路径) ,PYTHONPATH 和安装缺省路径。这样,用户可以通过修改 sys.path 来修改和替换模块搜索路径。参见后面关于标准模块的一节。 6.1.2 “编译”的 Python 文件 为了提高调用许多标准模块的小程序的启动时间, 一个重要的措施是, 如果在找到 “spam.py ”

的目录中存在一个名为“spam.pyc”的文件,就认为此文件包含了模块 spam 的一个所谓“字 节编译”版本。用于生成“spam.pyc”的“spam.py”的修改时间被记入了“spam.pyc”中, 如果记录的修改时间与现在文件的时间不相符的话就忽略编译文件。 一般不需要自己生成“spam.pyc”这样的编译文件。每当“spam.py”成功编译后解释程序就 尝试写编译版本“spam.pyc” ,如果不可写也不会出错;如果因为某种原因此文件没有写完则 生成的“spam.pyc”被识别为不完整的而被忽略。编译文件“spam.pyc”的格式是不依赖于 平台的,所以不同结构的机器可以共享 Python 模块目录。 下面是对专家的一些窍门: 如果 Python 解释程序是以-O 标志启动的,将生成优化的编译代码,保存在“.pyo”文件中。 目前优化不是很多,现在只是去掉 assert 语句和 SET_LINENO 指令。使用了-O 标志时,所有 字节码都是优化的, “.pyc”文件被忽略, “.py”文件被编译为优化的字节码。

给 Python 解释程序两个优化标志(-OO)产生的优化代码有时会导致程序运行不正常。目前 双重优化只从字节码中删除了__doc__字符串,使得“.pyo”文件较小。有些程序可能是依赖 于文档字符串的,所以只有在确知不会有问题时才可以使用这样的优化。 从“.pyc”或“.pyo”读入的程序并不能比从“.py”读入的运行更快,它们只是调入速度更 快一些。 如果一个程序是用在命令行指定脚本文件名的方式运行的,脚本的字节码不会写入“.pyc ” 或“.pyo”文件。所以如果把程序的主要代码都移入一个模块,脚本中只剩下导入该模块的 引导程序则可以略微缩短脚本的启动时间。 可以有叫做“spam.pyc” (当用了-O 标志时为“spam.pyo” )的文件而没有对应的源文件 “spam.py” 。这可以用来分发一个比较难反编译的 Python 代码库。 模块 compileall 可以把一个目录中所有模块编译为“.pyc”文件(指定了-O 选项时编译为 “.pyo”文件) 。 6.2 标准模块 Python 带有一个标准模块库,在另一个文档《Python 库参考》中进行了描述。一些模块直接 编入了解释程序中,这些模块不是语言的核心,为了运行效率或者为了提供对于系统调用这 样的系统底层功能而编入了解释程序中。提供那些模块是编译时的选择,例如,amoeba 模块 只在提供 amoeba 底层指令的系统中才能提供。 有一个模块值得特别重视:sys 模块,每一个 Python 解释程序中都编译入了这个模块。变量 sys.ps1 和 sys.ps2 定义了交互运行时的初始提示和续行提示。 >>>import sys>>>sys.ps1'>>>'>>>sys.ps2'... 'Yuck!'Yuck!C> '>>>sys.ps1 = 'C>'C>print

这两个变量只在解释程序以交互方式运行时才有定义。 变量 sys.path 是一个字符串列表, 由它确定解释程序的模块搜索路径。 它被初始化为环境变 量 PYTHONPATH 所指定的缺省路径, 环境变量没有定义时初始化为安装时的缺省路径。 可以用 标准的列表操作修改这个搜索路径,例如: >>>import sys>>>sys.path.append('/ufs/guido/lib/python') 6.3 dir()函数 内置函数 dir()用于列出一个模块所定义的名字,它返回一个字符串列表: >>>import fibo, sys>>>dir(fibo)['__name__', 'fib', 'fib2']>>>dir(sys)['__name__', 'argv', 'builtin_module_names', 'copyright', 'exit','maxint', 'modules', 'path', 'ps1', 'ps2', 'setprofile', 'settrace','stderr', 'stdin', 'stdout', 'version']

没有自变量时,dir()列出当前定义的名字。 >>>a = [1, 2, 3, 4, 5]>>>import fibo, sys>>>fib = fibo.fib>>>dir()['__name__', 'a', 'fib', 'fibo', 'sys'] 注意 dir()列出了所有各类名字:变量名、模块名、函数名,等等。dir()不会列出内置函数、 变量的名字。要想列出内置名字的话需要使用标准模块__builtin__: >>>import __builtin__>>>dir(__builtin__)['AccessError', 'AttributeError', 'ConflictError', 'EOFError', 'IOError','ImportError', 'IndexError', 'KeyError', 'KeyboardInterrupt','MemoryError', 'NameError', 'None', 'OverflowError', 'RuntimeError','SyntaxError', 'SystemError', 'SystemExit', 'TypeError', 'ValueError','ZeroDivisionError', '__name__', 'abs', 'apply', 'chr', 'cmp', 'coerce','compile', 'dir', 'divmod', 'eval', 'execfile', 'filter', 'float','getattr', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'len', 'long','map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'range', 'raw_input','reduce', 'reload', 'repr', 'round', 'setattr', 'str', 'type', 'xrange'] 6.4 包 Python 中可以用“包”来组织 Python 的模块名字空间,名字引用时可以用“带点的模块名。 例如,模块名 A.B 代表包“A”内名为“B”的子模块。正如使用模块可以使不同模块的作者 不用顾虑彼此的全局变量名会冲突,使用带点的模块名可以使多模块包如 NumPy 和 PIL 的作 者不需要担心彼此的模块名会冲突。 假设你有一系列处理声音文件和声音数据的模块(称为一个“包”。有许多种不同的声音文 ) 件格式(通常用扩展名来识别,如“wav”“.aiff”“.au”,所以你可能需要制作并维护一 , , ) 组不断增加的模块来处理不同文件格式的转换。你还可能需要对声音数据进行许多不同的操 作(如混音、回响、均衡、产生模拟立体声效果) ,所以你还需要不断增加模块来执行这些操 作。一下是你的程序包的可能的结构(用一个分层文件系统表示) :

Sound/ 顶层包 __init__.py 初始化 音响包 Formats/ 用于文件 格式转换 的子程序 包 __init__.pywavread.pywavwrite.pyaiffread.pyaiffwrite.pyauread.pyauwrite.py...Eff ects/ 用于音响效果的子程序包__init__.pyecho.pysurround.pyreverse.py...Filters/ 用于滤波的子程序包__init__.pyequalizer.pyvocoder.pykaraoke.py... 包目录中的“__init__.py”文件是必须得,用来指示 Python 把这个目录看成包,这可以防 止有相同名字如“string”的子目录掩盖住在搜索路径后面一些出现的模块定义。在最简单 的情况下, “__init__.py”可以是一个空文件,它也可以包含初始化包所需的代码,和设置 “__all__”变量,这些后面会加以讨论。 包的用户可以从包中导入单独的模块,如: import Sound.Effects.echo

这可以把子模块 Sound.Effects.echo 导入。要引用它也必须用全名,例如: Sound.Effects.echo.echofilter(input, output, delay=0.7, atten=4) 导入子模块的另一种办法是: from Sound.Effects import echo 这同样也导入子模块 echo,但调用时不需写包前缀,所以可以用如: echo.echofilter(input, output, delay=0.7, atten=4) 另外一种写法是直接导入所需的函数或变量: from Sound.Effects.echo import echofilter 这一次同样是调入了子模块 echo,但是使其函数 echofilter 直接可用: echofilter(input, output, delay=0.7, atten=4) 注意使用“from 包 import 项”这样的格式时,导入的项可以是包的一个子模块(或子包) , 也可以是包内定义的其它名字如函数、类、变量。导入语句首先查找包内是否定义了所需的 项,如果没有则假设它是一个模块然后调入。如果找不到,结果引起 ImportError。 相反的,当使用“import item.subitem.subsubitem”这样的格式时,除最后一个外其它各 项都应该是包,最后一项可以是包也可以是模块,不允许是前面一项内部定义的类、函数或 变量。 6.4.1 从包中导入* 现在,如果用户写“from Sound.Effects import *”会发生什么情况?理想情况下我们希望 这应该扫描文件系统, 找到所有包内的子模块并把它们都导入进来。 不幸的是这种操作在 Mac

和 Windows 平台上不能准确实现,这两种操作系统对文件名的大小写没有准确信息。在这些 平台上,不知道名为“ECHO.PY”的文件会作为模块 echo、Echo 还是 ECHO 被导入。 (例如, Windows 95 在显示文件名时总是讨厌地把第一个字母大写) 。DOS 的 8+3 文件名限制更是对 长模块名造成了有趣的困难。 这个问题的唯一解决办法是由模块作者显式地提供包的索引。引入*的 import 语句遵循如下 规定:如果包的“__init__.py”文件定义了一个名为“__all__”的列表,这个列表就作为 从包内导入*时要导入的所有模块的名字表。 因此当包的新版本发布时需要包的作者确保这个 列表是最新的。包的作者如果认为不需要导入*的话也可以不支持这种用法。例如,文件 Sounds/Effects/__init__.py 可以包含如下代码: __all__ = ["echo","surround","reverse"] 这意味着 from Sound.Effects import *将从 Sound 包中导入指定的三个子包。 如果没有定义__all__,则 from Sound.Effects import *语句不会导入 Sound.Effects 包中 的 所 有 子 模 块 ; 此 语 句 只 能 保 证 Sound.Effects 被 导 入 ( 可 能 是 执 行 其 初 始 化 代 码 “__init__.py ” )并导入包中直接定义的名字。这包括由“__init__.py”定义的任何名字 和显式导入的子模块名。这也包括模块中已经在前面用 import 显式地导入的子模块,例如: import Sound.Effects.echoimport Sound.Effects.surroundfrom Sound.Effects import * 在这个例子中, echo 和 surround 模块被导入当前名字空间, 因为它们在执行 from...import 语句时已定义(在定义了__all__的情况下这一点也是成立的) 。 注意用户应尽量避免使用从模块或包中导入*的做法, 因为这样经常导致可读性差的代码。 尽 管如此, 在交互运行时可以用导入*的办法节省敲键次数, 而且有些模块在设计时就考虑到了 这个问题,它们只输出遵循某种约定的名字。注意,from 包 import 特定子模块的用法并没 有错,实际上这还是我们推荐的用法,除非程序还需要用到来自其它包的同名的子模块。 6.4.2 包内部引用 子模块常常需要彼此引用。例如,模块 surround 可能要用到模块 echo。事实上,这样的引 用十分常见, 所以 import 语句首先从子模块的所在包中寻找要导入的子模块才在标准模块搜 索路径查找。所以,模块 surround 只要写 import echo 或 from echo import echofilter。 如果在包含本模块的包中没有找到要导入的模块,import 语句将去寻找指定名字的顶级模 块。 当包组织成子包时(比如例中的 Sound 包) ,没有一种简单的办法可以引用兄弟包中的子模 块 ― ― 必 须 使 用 子 模 块 的 全 名 。 例 如 , 如 果 模 块 Sound.Filters.vocoder 要 引 用 Sound.Effects 包中的 echo 模块,它可以用 Sound.Effectsimport echo。]

第七章输入输出 有几种办法可以从程序输出;数据可以用可读的形式显示,或保存到文件中以备日后使用。 本章讨论一些输入输出的办法。 7.1 输出格式控制 到现在为止我们已经看到了两种输出值的方法: 表达式语句和 print 语句。 (第三种方法是使 用文件对象的 write()方法,标准输出文件可以用 sys.stdout 引用。参见库参考手册) 。 我们常常需要控制输出格式,而不仅仅是显示空格分开的值。有两种办法控制输出格式:一 种办法是自己进行字符串处理,用字符串的片断和合并操作可以产生任何可以想象的格式。 标准模块 string 包含了诸如把字符串填充到指定的列宽这样的有用操作,后面会有提及。 另一种办法是使用%运算符,此运算符以一个字符串为左运算元,它按 C 的 sprintf()函数格 式把右运算元转换为字符串,返回转换结果。 问题是:如何把值转换为字符串? 幸运的是,Python 有一种办法可以把任何值转换为字符串:使用 repr()函数,或把值写在两 个反向引号(``)之间。例如: >>>x = 10 * 3.14>>>y = 200*200>>>s = 'The value of x is ' + `x` + ', and y is ' + `y` + '...'>>>print sThe value of x is 31.4, and y is 40000...>>># 反向引号也适用 于非数值型... p = [x, y]>>>ps = repr(p)>>>ps'[31.4, 40000]'>>># 转换字符串对字符 串加字符串引号和反斜杠... hello = 'hello, world\n'>>>hellos = `hello`>>>print hellos'hello, world\012'>>># 反 向 引 号 内 可 以 是 一 个 序 表 ... `x, y, ('spam', 'eggs')`"(31.4, 40000, ('spam', 'eggs'))" 下面是两种写出平方、立方表的方法: >>>import string>>>for x in range(1, 11):... print string.rjust(`x`, 2), string.rjust(`x*x`, 3),... # 前 一 行 的 结 尾 逗 号 表 示 不 换 行 ... print string.rjust(`x*x*x`, 4)...1 1 12 4 83 9 274 16 645 25 1256 36 2167 49 3438 64 5129 81 72910 100 1000>>>for x in range(1,11):... print'%2d %3d %4d' % (x, x*x, x*x*x)...1 1 12 4 83 9 274 16 645 25 1256 36 2167 49 3438 64 5129 81 72910 100 1000 注意 print 输出的各项之间额外加了一个空格,这是 print 的规定。 此例显示了函数 string.rjust()的用法,此函数可以把一个字符串放进指定宽度右对齐,左 边用空格填充。类似函数还有 string.ljust()和 string.center()。这些函数不向外输出, 只是返回转换后的字符串。如果输入字符串太长也不会被截断而是被原样返回。这样的处理 可能会使你的列对齐失效, 但这可能比截断要好一些, 截断的结果是我们看到一个错误的值。 (如果你确实需要截断的话总可以再加一层片断,如 string.ljust(x,n)[0:n]) 。 还有一个函数 string.zfill(), 可以在数值左边填零。 此函数可以处理带有加减号的情况: >>>string.zfill('12', 5)'00012'>>>string.zfill('-3.14',

7)'-003.14'>>>string.zfill('3.14159265359', 5)'3.14159265359'

%操作符的用法如下例: >>>import math>>>print 'The value of PI is approximately %5.3f.' % math.piThe value of PI is approximately 3.142. 如果有多个值可以用一个序表给出,这时格式字符串中要有多个格式,如: >>>table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}>>>for name, phone in table.items():... print'%-10s ==>%10d' % (name, phone)...Jack ==>4098Dcab ==>8637678Sjoerd ==>4127 大多数格式与 C 用法相同,要求要输出的值的类型符合格式的需要。但是,如果你没有引发 例外错误的话也不会产生内核堆列。 Python 的%s 格式要宽得多: 如果相应的输出项不是字符 串对象,就先用 str()内置函数把它变成字符串。在格式指定中宽度指定为*号表示后面的输 出项中多出一个指定宽度的整数。C 格式%n 和%p 未被支持。 如果你有一个长格式串不想把它分开,可以在指定格式的地方指定名字,这样就不需要按次 序去把格式和名字对应起来,这种格式为“%(变量名)格式” ,例如: >>>table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}>>>print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % tableJack: 4098; Sjoerd: 4127; Dcab: 8637678 这里输出项总是一个字典,字典的各项值是要输出的值,字典的键值是各值的名字。这种输 出格式经常与内置函数 var()配合使用,var()返回包含所有局部变量的字典。 7.2 读写文件 open()打开一个文件对象,经常使用两个参数: “open(文件名,模式)” 。例如: >>>f=open('/tmp/workfile', 'w')>>>print f<open file '/tmp/workfile', mode 'w' at 80a0960>

第一自变量是一个包含了文件名的字符串,第二自变量是文件打开方式的字符串。模式‘r ’ 表示读取, ‘w’表示只写(已有的同名文件被清除)‘a’表示打开文件在尾部添加, , ‘r+’ 表示打开文件既可以读也可以写。打开方式参数可选,缺省为‘r’模式。 在 Windows 和 Macintosh 中在模式中加入 ‘b’ 表示以二进制格式打开文件, ‘rb’ 如 、 ‘wb ’ 、 ‘r+b’ 。Windows 对文本文件和二进制文件有不同的处理,文本文件中的换行字符在读写时 有变化。 这种对文件数据的幕后的修改不影响 ASCII 文本文件, 但是会破坏二进制数据如 JPEG 或“.EXE”文件的数据。读写这样的文件一定要使用二进制格式。 (Macintosh 中文本模式的 精确描述依赖于使用的 C 库) 。 7.2.1 文件对象的方法 本节后面的例子假设已经建立了一个名为 f 的文件对象。

为了读取文件内容, 调用 f.read(size), 可以读入一定字节数的数据返回为一个字符串。 size 是一个可选数值参数,省略 size 或 size 取负值时读入整个文件并返回为一个字符串;如果 文件比你的机器内存大一倍,那是你的问题。指定了正的 size 的时候之多读入并返回 size 字节。如果读到了文件尾,f.read()返回一个空串("") 。如: >>>f.read()'This is the entire file.\012'>>>f.read()'' f.readline()从文件中读入一行,返回的字符串中将包括结尾的一个换行符(\n) ,如果文件 的最后一行没有换行符则由该行读入的字符串也没有结尾的换行符。 这样, readline() 返 由 回的结果不会有歧义,读入结果为空串时一定是到了文件尾,读入一个'\n'时为空行。 >>>f.readline()'This is the first line of the file.\012'>>>f.readline()'Second line of the file\012'>>>f.readline()'' f.readlines()反复调用 f.readline(),返回一个包含文件所有行的列表。 >>>f.readlines()['This is the first line of the file.\012', 'Second line of the file\012'] f.write(string)把 string 的内容写入到文件中,返回 None。 test\n') >>>f.write('This is a

f.tell()返回文件对象的当前读写为止,按从文件开始的字节数算。为了改变读写位置,使 用“f.seek(位移,从哪里)” 。读写位置按一个参考点加上位移来计算,参考点用“从那里” 参数指定,取 0 时从文件头开始算,取 1 时按当前位置算,取 2 时从文件尾算。缺省值是 0 , 从文件开始算。 >>>f=open('/tmp/workfile', 'r+')>>>f.write('0123456789abcdef')>>>f.seek(5) # 从文 件头前进 5 个字节,到达第 6 个字符>>>f.read(1)'5'>>>f.seek(-3, 2) # 转到结尾前 3 个 字符>>>f.read(1)'d' 用外一个文件后调用 f.close()关闭文件,释放打开文件所占用的系统资源。文件关闭后再 使用此文件对象就无效了。 >>>f.close()>>>f.read()Traceback (innermost in ?ValueError: I/O operation on closed file last):File"<stdin>", line 1,

文件对象还有其它一些不太常用的方法,例如 isatty()和 truncate(),参见库参考手册。 7.2.2 pickle 模块 字符串可以很容易地从文件读入或向文件写出。读入数值要麻烦一些,因为 read()方法总是 返回字符串,要把读入的字符串传给象 string.atoi()这样的函数,把象‘123’这样的字符 串转换为对应的整数值 123。但是,当你想保存更复杂的数据类型如列表、字典或类实例时, 读写就要复杂得多。

Python 的设计使程序员可以不必反复编写调试保存复杂数据类型的代码, 它提供了一个叫做 pickle 的标准模块。这个令人惊异的模块可以把几乎任何 Python 对象转换为字符串表示, 这个过程叫做腌制,从对象的字符串表示恢复对象叫做恢复。在腌制和反腌制之间,对象的 字符串表示可以保存在文件或数据中,甚至于通过网络连接传送到远程计算机上。 如果你有一个对象 x, 有一个可写的文件对象 f, 最简单的腌制对象的办法是下面一行代码: pickle.dump(x, f)

为了恢复对象,如果刚才的文件已打开用于读取,文件对象名仍为 f,则: x = pickle.load(f) (腌制和恢复还有其它用法,可以腌制多个对象,可以不把数据写入文件,详见库参考手册)。 pickle 是保存 Python 对象并被其它程序或同一程序以后再运行时调用的标准办法,这种做 法的专用术语叫做“持久对象” 。因为 pickle 使用广泛,许多 Python 扩展模块的作者都留意 使新增加的数据类型如矩阵可以正确地腌制和恢复。

第八章错误与例外 到现在为止我们只是提到了错误信息而没有详细讨论,如果你运行了前面的例子可能已经看 到了一些错误信息。至少有两种不同错误:句法错和例外错(exceptions) 。 8.1 句法错 句法错也称为语法分析错,是你在学习 Python 的时候最可能犯的错误。 >>>while 1 print 'Hello world'File"<stdin>", line 1while 1 print 'Hello world'^SyntaxError: invalid syntax

语法分析器重复出错行,并用一个小‘箭头’指向行内最早发现错误的位置。错误是由箭头 前面的记号引起的(至少是在这里检测到的) 。在本例中,错误在关键字 print 处检测到,因 为它前面应该有一个冒号( “:”。错误信息中显示了文件名和行号这样如果错误发生在一个 ) 脚本文件中你就知道到哪里去找。 8.2 例外 即使语句或表达式句法没有问题,在试图运行的时候也可能发生错误。运行时检测到的错误 叫做例外,这种错误不一定必然是致命的:你很快就会学到如何在 Python 程序中处理例外。 然而,多数例外不能被程序处理,这是会产生错误信息,如: >>>10 * (1/0)Traceback (innermost last):File"<stdin>", line 1ZeroDivisionError: integer division or modulo>>>4 + spam*3Traceback (innermost last):File"<stdin>", line 1NameError: spam>>>'2' + 2Traceback (innermost last):File"<stdin>", line

1TypeError: illegal argument type for built-in operation 错误信息的最后一行显示发生的情况。 例外有不同的类型, 类型作为错误信息的一部分显示: 上例中错误的类型有 ZeroDivisionError、NameError 和 TypeError。作为例外类型显示的字 符串是发生的例外的内置名。 这对于所有内置例外成立, 但对用户自定义例外不一定成立 (用 户最好能遵守这样的约定) 。标准例外名是内置的标识符(不是保留关键字) 。 此行的其余部分是错误的细节,其解释依赖于例外类型。错误信息前面的部分以堆栈反跟踪 的形式显示了发生错误的上下文环境。一般这包含了列出源代码行的一个列出源程序行的堆 栈反跟踪;然而,它不会显示从标准输入读进的行。 库参考手册列出了内置例外和其含义。 8.3 例外处理 可以编程序来处理选定的例外。请看下面的例子,显示一些浮点数的倒数: >>>numbers = [0.3333, 2.5, 0, 10]>>>for x in numbers:... print x,... try:... print 1.0 / x... except ZeroDivisionError:... print '*** has no inverse ***'...0.3333 3.000300032.5 0.40 *** has no inverse ***10 0.1

try 语句是这样工作的:

首先,运行 try 子句(在 try 和 except 之间的语句) 。 如果没有发生例外,


相关文章:
Python入门教程 超详细1小时学会Python
Python入门教程 超详细1小时学会Python_计算机软件及应用_IT/计算机_专业资料。...child -__init_.py --a.py b.py 那么 Python 如何找到我们定义的 module?...
python基础教程至60课(基础)
python基础教程至60课(基础)_计算机软件及应用_IT/计算机_专业资料 暂无评价|0人阅读|0次下载|举报文档python基础教程至60课(基础)_计算机软件及应用_IT/计算机_...
python爬虫入门教程
python爬虫入门教程_计算机软件及应用_IT/计算机_专业资料。python爬虫 python网络技术 Python 爬虫入门三之 Urllib 库的基本使用 Python 崔庆才 5 个月前 (02-12...
Python入门教程
my_string = "chenguolin" print len(my_string) print my_string.upper() Python 入门教程 4 --- Date and Time 26 介绍得到当前的时间 datetime.now() ...
python基础教程至60课(2.7版本)整理
python基础教程至60课(2.7版本)整理_计算机软件及应用_IT/计算机_专业资料。python 基础教程至 60 课整理 2014 年 7 月 10 日:(整理前几天内容) 1. 疑惑:a...
Python脚本入门学习经典手册
Python脚本入门学习经典手册_计算机软件及应用_IT/计算机_专业资料。本手册是很好...Python》如获至宝,独乐乐不如众 乐乐,现在将其教程翻译并结合自己的学习情况给...
python免费视频教程(初高中级)+基础教程
python免费视频教程(初高中级)+基础教程_英语考试_外语学习_教育专区。小编费了好大的劲,才找到了这么多的免费课程,包涵了初中高三个级别的课程,还有基础的文字...
PYTHON入门教程
PYTHON入门教程_韩语学习_外语学习_教育专区。PYTHON入门教程Python 基础教程 Python 环境搭建→ Python 简介 Python 是一个高层次的结合了解释性、编译性、互动...
编程入门教程
网络编程入门 1.27. c++游戏编程入门教程 1.28. vc 编程入门 1.29. linux 编程入门 1.30. python 游戏编程入门 1.31. 数控编程入门 1.32. excel 编程入门...
Python入门教程
Python入门教程_计算机软件及应用_IT/计算机_专业资料。Python 入门教程本文适合有经验的程序员尽快进入 Python 世界.特别地,如果你掌握 Java 和 Javascript,不用 1...
更多相关标签:
python基础教程 | python入门 | python入门经典 | python教程 | python | python语言入门 | python基础教程 pdf | python视频教程 |