第1章 Python核心知识回顾 else: print(\程序提示用户输入一个适当范围内的整数,然后用int()把读取到的值转换成一个整数。如果转换失败,则会抛出一个ValueError异常,然后展示错误信息。如果转换成功,就肯定能得到一个有效的整数。这样,可以跳出while循环,继续把它和目标值进行测试。 1.4.2 使用文本文件 在编程中保存数据时,文本文件是非常重要的。Python提供了几个函数用来处理文本文件。 在前面你看到了open()函数,它接受文件名和一个模式作为参数。模式可以为r、w、rw和a中的任意一个。它们分别代表着读、写、读写和附加(有一些其他模式,但是不经常用到。还有一些可选参数用来控制数据如何被解释。可以在文档中获得细节)。r模式需要文件是存在的;w和rw模式会创建一个新的空文件(或者覆盖任何已存在的同名文件)。a模式会打开一个已经存在的文件。如果指定名称的文件并不存在,它会创建一个新的空文件。返回的文件对象也是一个上下文管理器。就像你在上下文管理器部分看到的那样,它可以被用在一个with语句块中。如果没有使用一个with语句块,那么当用完这个文件时,你应该调用close()方法显式地关闭文件。这样可以保证任何驻留在内存缓冲区中的数据都会被发送到磁盘上的物理文件中。with结构体会自动地调用close(),这也是使用上下文管理器方法的优势之一。 一旦已经打开了文件对象,就可以根据需要使用read()、readlines()或readline()这些方法。read()方法读取整个文件内容到一个字符串中,并以一个换行符结束。readlines()方法把文件一行一行地读入一个列表中,并且保留每一行的换行符。readline()方法读取文件中的下一行,它也保留换行符。文件对象是可迭代的,所以你不需要任何读取方法就可以直接在for循环中使用它。因此,从文件中读取行的推荐模式如下: with open(filename, mode) as fileobject: for line in fileobject: # process line 注意:Python中的文件接口实际上是一个更高层次抽象接口的具体化。抽象接口从一个名为io.IOBase的类开始。你基本上可以忽略它们。它们只是创建了标准的操作集,并把它们应用于文本文件和类文件对象。 可以使用write()或writelines()方法向一个可写入的文件对象中写入数据。它们与类似名称的读方法是等同的。注意,没有写单独一行的writeline()方法。 如果在使用rw模式,你可能想要移动到文件的特定位置来重写现有的数据。使用tell() 23 Python项目开发实战 方法可以找到你在文件中的当前位置。使用seek()方法可以定位到一个指定位置(可能是你之前使用tell()记录下来的一个位置)。seek()有几种计算位置的模式,默认模式是计算距离文件开头的偏移值。 现在你已经掌握了编写Python程序的所有的基本技术。然而,为了实现本书的重点,即能够处理更大的项目,你会想要扩展Python的功能。下一节我们开始探索它。 1.5 扩展Python 最简单的扩展Python的方法就是编写你自己的函数。你定义的函数可以与使用它们的代码放在相同的文件中。或者也可以创建一个新的模块,然后从它导入函数。你会在下一节看到模块。现在,你将会创建函数并且在同一个文件中使用它们。实际上在这一节,你大多数时间会使用交互式提示符来试验示例。 Python创建新功能中的下一步就是定义你自己的类并且从它们创建对象。同样,普遍的做法是在模块中创建类。你会在下一节看到怎么实现它。这部分涉及的示例都很简单,你仅仅使用Python提示符就可以了。 Python程序员经常在他们的程序中使用文档字符串。文档字符串就是字符串字面值。它们没有被赋值给变量,而且遵循在它们被定义地方的缩进级别。可以用文档字符串来描述函数、类或者模块。help()函数会读取并展示这些文档字符串。 1.5.1 定义并使用函数 Python中有多种类型的函数。这部分会首先介绍标准函数,然后是生成器函数,最后是有些神秘的lambda函数。 在Python中,你会使用def关键字来定义函数。它看起来如下所示: def functionName(parameter1, param2,...): function block Python函数永远会返回一个值。可以用return关键字来指定一个显式的返回值。否则,Python会默认返回None(如果在输出中发现意想不到的None值,请检查相关函数体中是否含有显式的return语句)。可以通过在参数名后面加上等号和指定值来给参数赋予默认值。在下面,你会在odds()生成器函数中看到一个示例。 通过下面的“试一试”中,可以最容易理解一个函数定义是怎么创建和使用的。 试一试:创建并使用一个函数 在这个“试一试”中,你会创建一个函数。这个函数接受多个输入参数并返回一个值。此函数会根据给定的斜率、x坐标和常量,使用直线的数学方程,返回对应的y坐标。然后,你会使用这个函数去生成一条直线上的一系列坐标。 (1) 启动Python解释器。 24 第1章 Python核心知识回顾 (2) 输入下面的代码来定义函数: >>> def straight_line(gradient, x, constant): ... ''' returns y coordinate of a straight line -> gradient * x + constant''' ... return gradient*x + constant ... >>> (3) 既然你已经定义了函数,使用一些简单的值来测试它。可以提前心算出这些值。试着使用斜率值为2、x值为4、常数为-3的参数来调用函数: >>> # test with a single value first >>> straight_line(2,4,‐3) 5 (4) 现在让我们用一种更加复杂的方法来测试函数。使用下面的代码: >>> for x in range(10): ... print(x,straight_line(2,x,-3)) ... 0 -3 1 -1 2 1 3 3 4 5 5 7 6 9 7 11 8 13 9 15 (5) 最后,检查help()函数能否正确地识别函数: >>> help(straight_line) Help on function straight_line in module __main__: straight_line(gradient, x, constant) returns y coordinate of a straight line -> gradient * x + constant (END) 示例说明 第二步的第一行创建了函数定义。函数被命名为straight _ line,并且有三个必要的函数: gradient、x和constant。这些参数对应着在数学方程式y=mx+c中使用的值。在这个方程式中,m是斜率,c是常量。 第二行是一个文档字符串。它描述了这个函数的功能以及应该如何使用它。 第三行是函数的代码块。它可能任意的复杂并且长达多行。但是在这个示例中,它只 25 Python项目开发实战 有一行。因为要返回结果,所以在前面加了关键字return。注意,代码行首的缩进级别应该与文档字符串的行首相同。否则,你会得到一个缩进错误。 然后,我们使用一些简单值来测试函数。通过心算,我们确定返回值5确实等于(2*4-3)。看起来,函数至少在一些简单的情况下能够正常工作。 我们在for循环中使用函数来产生一组x,y坐标对。在这里,gradient的值被固定为2,constant值被固定为-3,而把x作为循环变量。如果身边有便利的纸的话,可以尝试在纸上画出函数产生的坐标,并确认这些坐标能否形成一条直线。 最后,我们使用help()函数来确定文档字符串被正确地检测到并展示出来。 1. 生成器函数 你将看到的下一个函数形式是生成器函数。生成器函数与标准函数看起来几乎一样,除了标准函数使用return返回数据,而生成器函数使用关键字yield(理论上生成器函数除了使用yield之外也可以使用return,但是只有yield表达式能产生生成器行为)。 Python优雅的魔力使生成器函数很特殊。它们像定格相机一样工作。当一个标准函数遇到return语句时,它会返回值,然后函数就会丢弃它的所有内部数据。当下次函数被调用时,一切都从头做起。 yield语句会做不同的事情。它像return一样返回一个值,但是它不会使函数丢弃数据;相反,所有数据都被保存起来了。下次函数被调用时,即使yield语句在代码块的中间或者处于循环中,程序也会从yield语句开始执行。在一个函数中,甚至可以有多个yield语句。由于yield语句可以被放在一个循环中,这就可以创建一个高效的返回一个无限系列结果的函数。下面的示例返回一个递增的奇数序列: def odds(start=1): ''' return all odd numbers from start upwards''' if int(start) % 2 == 0: start = int(start) + 1 while True: yield start start += 2 在这个函数中,首先检查start参数是否是一个奇数(偶数除以2的余数是0)。如果不是奇数,你会强迫它加1而成为下一个最近的奇数。然后,你创建了一个无限的while循环。通常,这是一个糟糕的主意,因为你的程序会陷入死循环。然而,由于这是一个生成器函数,你在使用yield语句返回start的值。这样,函数在返回start的值时就会退出。当下一次函数被调用时,程序会从之前离开的地方再开始。所以start会被加2,然后继续循环,产生下一个奇数并且退出函数。函数会在下一次调用时继续。 Python确保生成器函数能够变成迭代器,这样你就可以在for循环中使用它们。如下所示: for n in odds(): if n > 7: break 26 第1章 Python核心知识回顾 else: print(n) 你把odds()当成一个集合在使用。每次循环访问它,它会调用生成器函数,然后接收下一个奇数值。 通过插入break测试,可以避免一个无限循环。这样,当大于7时,odds()就不会被调用了。 现在你已经理解生成器函数是如何工作了,你也可能已经认识到本章前面介绍的生成器表达式就是高效的匿名生成器函数。生成器表达式实际上是一种变相的没有名称的生成器函数。 这为我们将要学到的最后一个函数类型,lambda函数,提供了一个完美的桥梁。 2. lambda函数 术语lambda来自于阿隆佐·邱奇(Alonzo Church)发明的一种微积分。好消息是,你不需要知道任何使用lambda函数的数学知识。lambda函数背后的原理是它通常是一个很小的匿名函数块。可以把它插入到代码中,然后像一个普通函数一样调用lambda函数。lambda函数并不常用。但是当需要创建很多只会被使用一次的小函数时,它们是非常方便的。它们通常被用在GUI或网络编程的环境中。在这些情况下,编程工具包需要一个用来回调得到结果的函数。 lambda函数的定义如下所示: lambda
好文档 - 专业文书写作范文服务资料分享网站