Python参考手册(第4版•修订版)

978-7-115-39439-2
作者: 【美】David M.Beazley(大卫 M.比兹利)
译者: 谢俊杨越高伟宋秉金
编辑: 杨海玲
分类: Python

图书目录:

详情

本书是Python编程语言的权威参考指南,书中详尽解释了Python 核心语言和Python库中最重要的部分,涉及类型和对象、操作符和表达式、编程结构和控制流、输入和输出、测试、调试等,也包括一些Python官方文档或其他参考资料中未提及的高级主题。

图书摘要

版权信息

书名:Python参考手册(第4版•修订版)

ISBN:978-7-115-39439-2

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。


• 著    [美] David M.Beazley

  译    谢 俊  杨 越  高 伟

  审  校 宋秉金

  责任编辑 杨海玲

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


本书是Python编程语言的权威参考手册,书中详尽讲解了Python核心和Python库中最重要的部分。全书分为三个部分,第一部分涉及类型与对象,运算符与表达式,程序结构与控制流,函数与函数式编程,类与面向对象编程,模块、包与分发,输入与输出,测试、调试、探查与调优等与Python语言相关的内容;第二部分涉及内置函数和异常、运行时服务、数学运算、数据结构、算法与代码优化,字符串和文本处理,数据库访问,文件和目录处理,操作系统服务,线程与并发,网络编程与套接字,网络应用程序编程,Web编程,网络数据处理和编码、其他库模块等与Python库相关的内容;第三部分涉及扩展和嵌入Python等内容。此外,书中还包括一些Python官方文档或其他参考资料中未提及的高级主题。

本书适合Python程序员以及具备其他编程语言经验的开发人员阅读和参考。


Authorized translation from the English language edition, entitled Python Essential Reference Fourth Edition, 9780672329784 by David M. Beazley, published by Pearson Education, Inc., publishing as Addison Wesley, Copyright © 2009 by Pearson Education,Inc.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.

CHINESE SIMPLIFIED language edition published by PEARSON EDUCATION ASIA LTD. and POSTS & TELECOM PRESS Copyright © 2016.

本书中文简体字版由Pearson Education Asia Ltd.授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。

本书封面贴有Pearson Education(培生教育出版集团)激光防伪标签,无标签者不得销售。

版权所有,侵权必究。


谨将本书献给Paula、Thomas和他即将出生的兄弟。


本书是Python编程语言的一份简明参考。尽管有经验的程序员也可以通过本书学习Python语言,但本书并不是一本讲述如何编程的全面教程或专著。相反,本书的目标是准确而简练地介绍Python语言核心以及Python库中最关键的部分。本书假定读者以前拥有Python或其他语言(如C或Java)的编程经验。另外,对系统编程(例如基本的操作系统概念和网络编程)有大致的了解对理解Python库参考中的部分章节可能会有一定的帮助。

Python可以从http://www.python.org免费下载。几乎每一种操作系统,包括UNIX、Windows和Macintosh,都有相应的版本。另外,Python网站还包含文档、指南和各种第三方软件的链接。

本书的这一版是在Python发展的关键时刻面世的。Python 2.6和Python 3.0几乎是同时发布的,但Python 3与以前的版本并不兼容。作为一名作者和程序员,我面临着一个两难问题:是直接跳到Python 3.0,还是使用大多数程序员更为熟悉的Python 2.x版本?

多年前,在应该使用哪些语言特性的问题上,身为C程序员的我经常把某些书当作终极权威。例如,使用K&R书中未曾提到过的某些特性很可能导致程序无法移植,因此得格外谨慎。作为程序员,这种方法对我非常有效,因此我决定在本书的这个版本中沿用这一做法。也就是说,这一版删除了Python 3中已经不再支持的Python 2特性。类似地,我也没有关注Python 3中不能向后兼容的特性(尽管附录中仍然会介绍这些特性)。最终,我希望,无论使用的是哪个版本的Python,本书都能够成为Python程序员案头必备的参考书。

这一版还包含自第1版出版至今10余年来最激动人心的一些变化。在过去的数年间,Python的发展重点体现在新的语言特性上——特别是与函数式编程和元编程相关的特性。因此,讲述函数式编程和面向对象编程的章节得到了极大的扩充,包括的主题有生成器、迭代器、协程、装饰器和元类。讲述库的章节则将重点转移到更加现代的模块上。整本书中的示例和代码片段都做了更新。我认为大多数程序员会对扩充之后的内容感到满意。

最后值得一提的是,Python本身已经有数千页有用的文档。本书的内容在很大程度上是基于这些文档的,但又有很多关键的区别。首先,这本书提供信息的方式更加紧凑,使用不同的示例,并对很多主题提供了不同的描述。其次,Python库参考部分的大量主题都做了扩充,包含很多外部的参考资料。底层系统和网络模块尤其如此,因为对这些模块的有效使用往往依赖于手册和外部参考中列出的种种选项。另外,为了使参考手册更加简明,这一版还删掉了很多已经废弃和相对晦涩的库模块。

我的目标是写出一本真正包含使用Python及其众多模块所需的一切内容的参考手册。尽管这并不是一本介绍Python语言的百科全书,但我希望本书能成为你未来数年里一本有用的必备编程参考书。十分欢迎读者对本书提出意见和建议。

David Beazley

2009年6月

于伊利诺伊州芝加哥


本书能与读者见面,要感谢很多人的大力支持。首先要感谢Noah Gift参与这个项目,并提出了许多建设性意见。Kurt Grandis也对很多章节发表了中肯的见解。我还要感谢前几版的技术审稿人Timothy Boronczyk、Paul DuBois、Mats Wichmann、David Ascher和Tim Bell,他们的精彩意见和建议促成了过去几版的成功。在1999年那个炎热的夏天,Guido van Rossum、Jeremy Hylton、Fred Drake、Roger Masse和Barry Warsaw招待了我好几个星期,同时对第1版提供了极大的帮助。最后,同样重要的是,本书的问世离不开读者的热情反馈。要感谢的人实在太多,这里无法一一列出他们的名字,我已尽力采纳大家的建议来让本书变得更好。我还要感谢Addison-Wesley和Pearson Education的工作人员,他们对这个项目给予了持续的支持与帮助。Mark Taber、Michael Thurston、Seth Kerney和Lisa Thibault都对本书的顺利出版倾注了大量的心血。还要特别感谢Robin Drake,他在第3版出版的过程中做了大量的编辑工作。最后,我要感谢我了不起的妻子和好搭档Paula Kamen,感谢她给予我的所有鼓励、幽默和爱。


本部分内容


本章将快速介绍Python这门语言,目标是在阐明Python的大部分基本特性的同时,又不会太过纠缠于特殊的规则或细节。为此,本章简要讲述一些基本概念,如变量、表达式、控制流、函数、生成器、类和输入/输出。本章不追求大而全,但有经验的程序员应该能够把本章中的资料推而广之,创建出更加高级的程序。鼓励初学者多尝试一些示例,找到对这门语言的感觉。如果你对Python不熟悉也没有使用过Python 3,可以使用Python 2.6来学习本章内容。本章介绍的几乎所有主要概念同时适用于这两个版本,但在Python 3中有少数关键语法变化(其中大多数与打印和I/O有关),可能会使本章中介绍的许多示例无法运行。请参考附录A,以了解详细信息。


如果在尝试前述例子时出现了语法错误,你使用的很可能就是Python 3。遇到这种情况并不妨碍你继续学习本章的内容,但要注意print语句在Python 3中已经变为一个函数。在下面的例子中,只要在要打印的内容两边加上括号即可正常运行,例如:

如果要打印的内容只有一项,在要打印内容两边放置括号的方法在Python 2中同样有效。然而,这种语法在现有的Python代码中并不常见。在后面的章节中,这种语法有时会用在与打印无直接关系的展示特性的例子中,但这些例子应该同时适用于Python 2和3。

Python程序是由解释器来执行的。通常,只要在命令shell中输入python即可启动解释器。然而,解释器和Python开发环境存在多种实现(如Jython、IronPython、IDLE、ActivePython、Wing IDE、pydev等),因此需要参考相应文档中的启动说明。解释器启动后将出现一个提示符,在此可以开始输入程序,进入简单的读入-求值循环。例如,在下面的输出中,解释器显示了版权消息和>>>提示符,用户可以在提示符后输入熟悉的打印“Hello World”命令:

Python 2.6rc2 (r26rc2:66504, Sep 19 2008, 08:50:24)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print "Hello World"
Hello World
>>>
>>> print("Hello World") 
Hello World
>>>

注意

Python的交互模式是它最有用的功能之一。在交互式shell中,可以输入任意合法的语句或语句序列,然后立即查看结果。很多人甚至使用交互式Python作为桌面计算器,作者本人也是如此。例如:

>>> 6000 + 4523.50 + 134.12
10657.620000000001
>>> _ + 8192.32
18849.940000000002
>>>

以交互模式使用Python时,特殊变量_保存最后一次运算的结果。如果要在后续语句中保存或使用最后一次运算的结果,使用此变量十分方便。但要强调一点,此变量只有在以交互模式编程时才会被定义。

如果要创建可以重复运行的程序,可将语句放到一个文件中:

# helloworld.py
print "Hello World"

Python源文件是普通的文本文件,后缀通常是.py#字符表示该行直至行尾的内容都是注释。

要执行helloworld.py文件,可通过如下方式将文件名提供给解释器:

% python helloworld.py
Hello World
%

在Windows中,双击一个.py文件或者在Windows开始菜单的“运行”命令中输入程序名称,均可启动Python程序。这会启动解释器,并在控制台窗口中运行程序。但要注意,当程序执行完成后,控制台窗口将立即消失(通常你来不及看清楚输出)。要进行调试,最好是在像IDLE这样的Python开发工具中运行程序。

在UNIX中,可以在程序的首行中使用#!,如下所示:

#!/usr/bin/env python
print "Hello World"

解释器不断运行语句,直到到达输入文件的结尾。如果是以交互模式运行,有两种方法可以退出解释器,一种是输入EOF(end of file,文件结束)字符,另一种是从Python IDE的下拉菜单中选择Exit。在UNIX中,EOF是Ctrl+D,而在Windows中则是Ctrl+Z(或F6)。程序可以通过抛出SystemExit异常来请求退出。

>>> raise SystemExit

程序清单1-1中的程序通过执行一次简单的复利计算,说明变量和表达式的用法。

程序清单1-1 简单的复利计算

principal = 1000           # 初始金额
rate = 0.05                # 利率
numyears = 5               # 年数
year = 1
while year <= numyears:
    principal = principal * (1 + rate)
    print year, principal  # 注意:在Python 3中应写成print(year, principal) 
    year += 1

此程序的输出如下所示:

1 1050.0
2 1102.5
3 1157.625
4 1215.50625
5 1276.2815625

Python是一种动态类型的语言,在程序执行过程中,变量名称会被绑定到不同的值,而且这些值可以属于不同的类型。赋值运算符的作用仅仅是在名称和值之间创建一种关联。尽管每个值都有一个相关类型,如integerstring,但变量名称是无类型的,在执行过程中可以引用任意类型的数据。这与C语言不同,例如,在C语言中,名称代表了用于保存值的固定类型、大小和内存位置。Python的动态行为可以从程序清单1-1的principal变量看出来。最初给它分配的是一个integer值,但程序稍后给它重新赋了值,如下所示:

principal = principal * (1 + rate)

这条语句对表达式求值,并把名称principal重新与结果关联。principal的原始值是整数类型的1000,但现在的新值是浮点数(rate被定义为浮点数,因此上述表达式的值也是浮点数)。因此在程序中,principal看上去的“类型”就从integer动态变为了float。然而准确地说,不是principal的类型变了,而是principal名称引用的值的类型变了。

换行代表一条语句的结束。然而,也可以在同一行上使用分号来隔开多条语句,如下所示:

principal = 1000; rate = 0.05; numyears = 5;

while语句对随后的条件表达式进行检验。如果被检验的语句为真,while语句的主体就会执行。然后再次检验条件,再执行主体,直到条件为假。因为循环主体是由缩进表示的,每次循环时都会执行程序清单1-1中while之后的3条语句。Python不会指定所需缩进的量,只要在一个代码块中保持一致即可。然而,每个缩进层次使用4个空格是最常见的情况,而且通常也建议这么做。

程序清单1-1中的程序有一个问题,即输出不是很美观。为了改进这一点,可以让各列右对齐,并将principal的精度限制为两位。实现这种格式有几种方法。最常用的方法是使用字符串格式化运算符%,如下所示:

print "%3d %0.2f" % (year, principal)
print("%3d %0.2f" % (year, principal))   # Python 3

现在程序的输出如下:

1 1050.00
2 1102.50
3 1157.63
4 1215.51
5 1276.28

格式化字符串包含普通文本和特殊的格式化字符序列,如"%d""%s""%f"。这些序列分别用于指定特定类型数据的格式,如整数、字符串或浮点数。特殊字符序列还可以包含用于指定宽度和精度的修饰符。例如,"%3d"将一个整数格式化为在一个宽度为3的列中右对齐,而"%0.2f"将一个浮点数格式化为在小数点后只出现两位数字。格式化字符串的行为与C语言中的printf()函数几乎完全相同,第4章将对此进行详细说明。

更新潮的字符串格式化的方法是使用format()函数单独格式化每个部分。例如:

print format(year,"3d"),format(principal,"0.2f")
print(format(year,"3d"),format(principal,"0.2f")) # Python 3

format()函数使用的格式说明符类似于传统字符串格式化运算符(%)使用的格式说明符。例如,"3d"将一个整数格式化为在一个宽度为3的列中右对齐,而"%0.2f"将一个浮点数格式化为两位精度。字符串也有一个format()方法,可用于一次性格式化很多值。例如:

print "{0:3d} {1:0.2f}".format(year,principal)
print("{0:3d} {1:0.2f}".format(year,principal)) # Python 3

在这个例子中,"{0:3d}""{1:0.2f}"中冒号前的数字代表传递给format()方法的相关参数,而冒号后的部分则是格式说明符。

ifelse语句可执行简单的检验,如下所示:

if a < b: 
    print "Computer says Yes"
else:
    print "Computer says No"

ifelse子句的主体是用缩进表示的。else子句是可选的。

要创建一条空子句,可以使用pass语句,如下所示:

if a < b:
    pass      # 什么也不执行
else:
    print "Computer says No"

使用orandnot关键字可以组成布尔表达式:

if product == "game" and type == "pirate memory" \
                     and not (age < 4 or age > 8):
    print "I'll take it!"

注意


编写复杂的检验条件通常需要编写很长的代码行,看起来令人生厌。为了提高代码的可读性,可以像上面一样在一行的结尾使用反斜杠(\),然后就可以在下一行继续书写上一条语句的内容。如果这样做,正常的缩进规则将不被应用于下一行,因此可以随意设置后续行的格式。

Python没有专门的switchcase语句用于检测多个值。要处理多个检验条件,可以使用elif语句,如下所示:

if suffix == ".htm":
    content = "text/html"
elif suffix == ".jpg":
    content = "image/jpeg"
elif suffix == ".png":
    content = "image/png"
else:
    raise RuntimeError("Unknown content type")

要表示真值,可以使用布尔值TrueFalse,例如:

if 'spam' in s:
    has_spam = True
else:
    has_spam = False

所有关系运算符(如<>)的结果都返回TrueFalse。本例中使用的in运算符通常用于检查某个值是否包含在另一个对象(如字符串、列表或字典)中。它也返回TrueFalse,因此前一个例子可以缩短为:

has_spam = 'spam' in s

以下程序可打开一个文件并逐行读取该文件的内容:

f = open("foo.txt")          # 返回一个文件对象
line = f.readline()          # 调用文件的readline()方法
while line:
    print line,              # 后面跟','将忽略换行符
    # print(line,end='')     # 在Python 3中使用
    line = f.readline()
f.close()

open()函数返回一个新的文件对象。调用该对象的方法可以执行各种文件操作。readline()方法读取一行内容,包括结尾的换行符在内。读至文件结尾时将返回空字符串。

在这个例子中,程序只是循环读取了文件foo.txt中的所有行。如果程序在像这样的数据集(如输入中的行、数字、字符串等)上进行循环,那么这通常就称为迭代。因为迭代是很常见的一种操作,所以Python为其提供了一条专用语句for,用于迭代内容项。例如,同样的程序可以写成下面这种更简洁的形式:

for line in open("foo.txt"):
    print line,

要将程序的输出写入一个文件中,需要在print语句后面使用>>指定一个文件,如下所示:

f = open("out","w")     # 打开文件以便写入
while year <= numyears:
    principal = principal * (1 + rate)
    print >>f,"%3d %0.2f" % (year,principal)
    year += 1
f.close()

>>语法只能用在Python 2中。如果使用Python 3,可将print语句改为以下内容:

print("%3d %0.2f" % (year,principal),file=f)

另外,文件对象支持使用write()方法写入原始数据。例如,前一例子中的print语句也可以写成下面这样:

f.write("%3d %0.2f\n" % (year,principal))

尽管这些例子处理的都是文件,但同样的技巧也适用于标准的解释器输出流和输入流。例如,如果想交互式地读取用户输入,可以从文件sys.stdin中读取。如果要将数据输出到屏幕上,可以写入文件sys.stdout中,这与在输出print语句所生成数据时所用的文件是同一个文件。例如:

import sys
sys.stdout.write("Enter your name :")
name = sys.stdin.readline()

在Python 2中,这段代码还可以简化为:

name = raw_input("Enter your name :")

在Python 3中,raw_input()函数叫做input(),但它们的工作方式完全相同。

要创建一个字符串字面量,将字符串放在单引号、双引号或三引号中即可,如下所示:

a = "Hello World"
b = 'Python is groovy'
c = """Computer says 'No'"""

字符串前后使用的引号必须是对应匹配的。两个三引号之间出现的所有文本都视为字符串的内容,而使用单引号和双引号指定的字符串必须在一个逻辑行上。当字符串字面量的内容需放在多个文本行上时,三引号字符串就很有用,如下所示:

print '''Content-type: text/html

<h1> Hello World </h1>
Click <a href="http://www.python.org">here.
'''

字符串存储在一个字符序列中,这个字符序列使用整数作为索引,索引从0开始。要提取其中的一个字符,可以使用索引运算符s[i],如下所示:

a = "Hello World"
b = a[4]                # b = 'o'

要提取一个子字符串,可以使用切片运算符s[i:j]。这会提取字符串s中索引位置k处的所有字符,其中索引k的范围是i<=k<j。如果省略i,则假定使用字符串的起始位置,如果省略j,则假定使用字符串的结尾位置:

c = a[:5]               # c = "Hello"
d = a[6:]               # d = "World"
e = a[3:8]              # e = "lo Wo"

可以使用加(+)运算符连接两个字符串:

g = a + " This is a test"

Python不会把字符串的内容隐式地解释为数值数据(Perl或PHP等语言中会这样解释)。例如,+运算符始终会连接字符串:

x = "37"
y = "42"
z = x + y    # z = "3742"(字符串连接)

要执行数学计算,首先要使用int()float()等函数将字符串转换为数值,例如:

z = int(x) + int(y)   # z = 79(整数求和)

使用str()repr()format()函数可将非字符串值转换为字符串表示形式,例如:

s = "The value of x is " + str(x)
s = "The value of x is " + repr(x)
s = "The value of x is " + format(x,"4d")

尽管str()repr()都可以创建字符串,但它们的输出通常存在细微的差别。str()生成的输出与使用print语句得到的输出相同,而用repr()创建的字符串可表示程序中某个对象的精确值,例如:

>>> x = 3.4
>>> str(x)
'3.4'
>>> repr(x)
'3.3999999999999999'
>>>

上例中3.4的不精确表示并非是Python中的一个bug。这是双精度浮点数的一个特点,因为从设计上说,底层计算机硬件无法精确地表示十进制小数。

format()函数可将值转换成特定格式等字符串,例如:

>>> format(x,"0.5f")
'3.40000'
>>>

列表是任意对象组成的序列。把值放入方括号中就可以创建列表,如下所示:

names = [ "Dave", "Mark", "Ann", "Phil" ]

列表使用从0开始的整数索引,使用索引运算符可以访问并修改列表中的项:

a = names[2]              # 返回列表的第3项"Ann"
names[0] = "Jeff"         # 将第1项改为"Jeff"

要将新项追加到列表末尾,可使用append()方法:

names.append("Paula")

要将一项插入到列表中,可使用insert()方法:

names.insert(2, "Thomas")

使用切片运算符可以提取一个子列表或对子列表重新赋值:

b = names[0:2]                       # 返回[ "Jeff", "Mark" ] 
c = names[2:]                        # 返回[ "Thomas", "Ann", "Phil", "Paula" ]
names[1] = 'Jeff'                    # 将names中的第2项替换为'Jeff'
names[0:2] = ['Dave','Mark','Jeff']  # 将列表的头两项替换为右边的列表

使用加号(+)可以连接列表:

a = [1,2,3] + [4,5]    # 结果是[1,2,3,4,5]

创建一个空列表有两种方式:

names = []        # 一个空列表
names = list()    # 一个空列表

列表可以包含任意种类的Python对象,包括其他列表在内,如下例所示:

a = [1,"Dave",3.14, ["Mark", 7, 9, [100,101]], 10]

嵌套列表中包含的项需要使用多次索引操作才能访问到,例如:

a[1]             # 返回 "Dave"
a[3][2]          # 返回 9
a[3][3][1]       # 返回 101

程序清单1-2中的程序展示了列表的一些高级特性,该程序会读取在命令行上指定的一个文件中的数值列表,然后输出其中的最大值和最小值。

程序清单1-2 列表的高级特性

import sys                       # 加载sys模块 
if len(sys.argv) != 2:            # 检查命令行参数的数量
     print "Please supply a filename"
     raise SystemExit(1)
f = open(sys.argv[1])            # 命令行上的文件名
lines = f.readlines()            # 将所有行读取到一个列表中
f.close()

# 将所有输入值从字符串转换为浮点数
fvalues = [float(line) for line in lines]

# 打印最小值和最大值
print "The minimum value is ", min(fvalues)
print "The maximum value is ", max(fvalues)

该程序的第一行使用import语句从Python库加载sys模块。加载该模块的目的是获得命令行参数。

open()函数使用了一个文件名,该文件名是以命令行选项的形式提供的并保存在列表sys.argv中。readline()方法将所有输入行读取到一个字符串列表中。

表达式[float(line) for line in line]通过对列表lines中的所有字符串进行循环,并对每个元素应用函数float(),从而构造一个新列表。这种功能特别强大的列表构造方法叫做列表推导(list comprehension)。因为你还可以使用for循环来读取文件中的行,所以可以将上面程序中转换值的代码简化为一条语句:

fvalues = [float(line) for line in open(sys.argv[1])]

将输入行转换成一个浮点数列表后,再使用内置函数min()max()计算出最大值和最小值即可。

要创建简单的数据结构,可以使用元组将一组值打包到一个对象中。在圆括号中放入一组值即可创建元组,例如:

stock = ('GOOG', 100, 490.10)
address = ('www.python.org', 80)
person = (first_name, last_name, phone)

即使没有圆括号,Python通常也能识别出元组:

stock = 'GOOG', 100, 490.10
address = 'www.python.org',80
person = first_name, last_name, phone

为了完整起见,也可以定义0个和1个元素的元组,但语法较为特殊:

a = ()          # 0元组 (空元组)
b = (item,)     # 1元组 (注意随后的逗号)
c = item,       # 1元组 (注意随后的逗号)

和列表一样,也可以使用数字索引来提取元组中的值。然而,更常见的做法是将元组解包为一组变量,例如:

name, shares, price = stock
host, port = address
first_name, last_name, phone = person

尽管元组支持的大部分操作与列表的相同(如索引、切片和连接),但创建元组后不能修改它的内容(也就是说无法替换、删除现有元组中的元素,或者向现有元组中添加新元素)。这说明最好把元组看成一个由多个部分组成的单一对象,而不是可在其中插入或删除项的不同对象的集合。

因为元组与列表之间存在诸多相似之处,所以有些程序员往往完全忽略了元组,而只使用列表,因为后者看似更灵活。尽管这并无不可,但如果程序创建了大量的小列表(即包含的项少于十来个),则会造成内存浪费。这是因为系统会为列表分配稍微多一些内存,以优化添加新项的操作的性能。而由于元组是不可变的,所以它们的展现更为紧凑,不会占据额外的内存空间。

表示数据时,经常同时使用元组和列表。例如,下面的程序显示了如何读取包含不同数据列,且各数据列由逗号隔开的文件:

# 文件中各行的格式为"name,shares,price" 
filename = "portfolio.csv"
portfolio = []
for line in open(filename):
    fields = line.split(",")       # 将每行划分为一个列表
    name   = fields[0]             # 提取并转换每个字段
    shares = int(fields[1])
    price  = float(fields[2])
    stock  = (name,shares,price)   # 创建一个元组(name, shares, price)
    portfolio.append(stock)        # 将记录追加到列表中

字符串的split()方法会按照指定的分隔符将一个字符串划分为一个字段列表。该程序最后创建的portfolio数据结构类似一个二维的行列数组,每行由一个元组表示,并可通过如下方式访问:

>>> portfolio[0]
('GOOG', 100, 490.10)
>>> portfolio[1]
('MSFT', 50, 54.23)
>>>

每个数据项可以通过如下方式访问:

>>> portfolio[1][1]
50
>>> portfolio[1][2]
54.23
>>>

下面给出了一种循环访问所有记录并将字段展开到一组变量中的简单方法:

total = 0.0 
for name, shares, price in portfolio:
    total += shares * price

集合用于包含一组无序的对象。要创建集合,可使用set()函数并像下面这样提供一系列的项:

s = set([3,5,9,10])       # 创建一个数值集合
t = set("Hello")          # 创建一个唯一字符的集合

与列表和元组不同,集合是无序的,也无法通过数字进行索引。此外,集合中的元素不能重复。例如,在检查前面代码中t集合的值时,会得到这样的结果:

>>> t
set(['H', 'e', 'l', 'o'])

注意,只出现了一个'l'

集合支持一系列标准操作,包括并集、交集、差集和对称差集,例如:

a = t | s         # t和s的并集
b = t & s         # t和s的交集
c = t – s         # 差集(项在t中,但不在s中) 
d = t ^ s         # 对称差集(项在t或s中,但不会同时出现在二者中)

使用add()update()可以在集合中添加新项:

t.add('x')            # 添加一项
s.update([10,37,42])  # 在s中添加多项

使用remove()可以删除一项:

t.remove('H')

字典就是一个关联数组或散列表,其中包含通过键(key)索引的对象。在大括号({ })中放入值即可创建字典,如下所示:

stock = { 
        "name"   : "GOOG",
        "shares" : 100,
        "price"  : 490.10
    }

要访问字典成员,可使用键索引运算符,如下所示:

name  = stock["name"]
value = stock["shares"] * shares["price"]

插入或修改对象的方法是:

stock["shares"] = 75
stock["date"]   = "June 7, 2007"

尽管字符串是最常用的键类型,还可以使用其他的Python对象,包括数值和元组。但包括列表和字典在内的一些对象不能用作键,因为它们的内容可以发生变化。

如前所述,在定义一个可包含多个命名字段的对象时,字典是一种很有用的方式。然而,字典也可用作快速查找无序数据的一个容器。例如,下面是一个股票价格的字典:

prices = { 
    "GOOG" : 490.10,
    "AAPL" : 123.50,
    "IBM"  : 91.50,
    "MSFT" : 52.13
}

创建一个空字典有两种方式:

prices = {}     # 一个空字典
prices = dict()  # 一个空字典

使用in运算符可以检验某个内容项是不是字典成员,如下所示:

if "SCOX" in prices:
    p = prices["SCOX"]
else:
    p = 0.0

这个特殊的步骤序列还可以写成更简洁的形式,如下所示:

p = prices.get("SCOX",0.0)

要获得一个字典关键字的列表,将字典转换为列表即可:

syms = list(prices)       # syms = ["AAPL", "MSFT", "IBM", "GOOG"]

使用del语句可以删除字典的元素:

del prices["MSFT"]

字典可能是Python解释器中最完善的数据类型。因此,如果只是要在程序中存储和处理数据,使用字典比使用一些自定义数据结构要好得多。

最常用的循环结构是for语句,它可以用来对容器成员进行迭代操作。迭代是Python中内涵最丰富的功能之一。但最常见的迭代形式是简单循环访问一个序列(如字符串、列表或元组)的所有成员,例如:

for n in [1,2,3,4,5,6,7,8,9]: 
    print "2 to the %d power is %d" % (n, 2**n)

在这个例子中,每次迭代都会将列表[1,2,3,4,...,9]中的下一个值赋给变量n。因为在整数范围内执行循环十分常见,为此经常会使用下面的快捷方法:

for n in range(1,10):
    print "2 to the %d power is %d" % (n, 2**n)

range(i, j, [,步长])函数创建的对象表示值在ij-1之间的整数范围。如果起始值i被省略,则认为是0。第三个参数是可选的步长值。例如:

a = range(5)         # a = 0,1,2,3,4
b = range(1,8)       # b = 1,2,3,4,5,6,7
c = range(0,14,3)    # c = 0,3,6,9,12
d = range(8,1,-1)    # d = 8,7,6,5,4,3,2

在使用range()函数时请注意,在Python 2中,它创建的值是已经用整数值完全填满的列表。当范围非常大时,这可能会在不经意间耗掉所有可用内存。因此,在老式的Python代码中,可能会看到程序员使用另一个函数xrange()。例如:

for i in xrange(100000000):    # i = 0,1,2,...,99999999
  statements

进行查找时,xrange()函数创建的对象会在收到查询请求时根据需要计算它所表示的值。因此,它成为了表示极大范围整数值的首选方式。在Python 3中,xrange()函数已经更名为range(),并且已删除了老式range()函数的功能。

for语句并不仅限于处理整数序列,还可用于迭代多种对象,包括字符串、列表、字典和文件,例如:

a = "Hello World" 
# 打印出a中的每个字符
for c in a:
    print c

b = ["Dave","Mark","Ann","Phil"]
# 打印出一个列表的成员
for name in b:
    print name

c = { 'GOOG' : 490.10, 'IBM' : 91.50, 'AAPL' : 123.15 }
# 打印出一个字典的所有成员
for key in c:
    print key, c[key]

# 打印一个文件中的所有行
f = open("foo.txt")
for line in f:
    print line,

for循环是Python最强大的语言特性之一,因为你可以创建自定义的迭代器对象和生成器函数,为它提供值序列。本章稍后和第6章将会讲述有关迭代器和生成器的更多内容。

使用def语句可以创建函数,如下例所示:

def remainder(a,b):
    q = a // b      # //是截断除法运算符
    r = a – q*b
    return r

要调用函数,只要使用函数名加上用圆括号括起来的参数即可,如result = remainder(37, 15)。如果要让函数返回多个值,可以使用元组,如下所示:

def divide(a,b):
    q = a // b      # 如果a和b是整数,q就是整数
    r = a – q*b
    return (q,r)

使用元组返回多个值时,可以很容易地将结果解包到单独的变量中,例如:

quotient, remainder = divide(1456,33)

要给函数参数提供一个默认值,可使用以下赋值方式:

def connect(hostname,port,timeout=300):
   # 函数体

在函数定义中给一个参数提供默认值以后,调用此函数时就可以省略该参数,此时该参数将使用默认值,如下所示:

connect('www.python.org', 80)

还可以使用关键字参数调用函数,此时可以按任意顺序提供参数,但这需要你知道函数定义中的参数名称,如下所示:

connect(port=80,hostname="www.python.org")

在函数中创建变量或给变量赋值时,该变量的作用域是局部的。也就是说,该变量只定义在函数体内部,而且当函数返回值后会立即销毁该变量。要在函数内部修改某个全局变量的值,可以使用global语句,如下所示:

count = 0 
... 
def foo():
    global count
    count += 1           # 更改全局变量count

如果使用yield语句,可以让函数生成一个结果序列,而不仅仅是一个值,例如:

def countdown(n):
    print "Counting down!"
    while n > 0:
         yield n       # 生成一个值(n)
         n -= 1

任何使用yield的函数都称为生成器。调用生成器函数将创建一个对象,该对象通过连续调用next()方法(在Python 3中是__next__())生成一系列的结果,例如:

>>> c = countdown(5)
>>> c.next()
Counting down!
5
>>> c.next()
4
>>> c.next()
3
>>>

next()调用使生成器函数一直运行,到下一条yield语句为止。此时next()将返回传递给yield的值,而且函数将暂时中止执行。再次调用next()时,函数将继续执行yield之后的语句。此过程持续到函数返回为止。

通常不会像上面这样手动调用next(),而是会使用一个for循环,例如:

>>> for i in countdown(5):
...     print i,
Counting down!
5 4 3 2 1
>>>

生成器是编写基于处理管道、流或数据流程序的一种极其强大的方式。例如,下面的生成器函数模拟了常用于监控日志文件的UNIX tail –f命令的行为:

# tail一个文件(如tail -f) 
import time
def tail(f):
    f.seek(0,2)     # 移动到EOF
    while True:
        line = f.readline()    # 尝试读取一个新的文本行
        if not line:           # 如果没有内容,暂时休眠并再次尝试
             time.sleep(0.1)
             continue
        yield line

下面的生成器用于在很多行中查找特定的子字符串:

def grep(lines, searchtext): 
    for line in lines:
        if searchtext in line: yield line

下面的例子将以上两个生成器合并在一起,创建了一个简单的处理管道:

# UNIX "tail –f | grep python"命令的python实现 
wwwlog = tail(open("access-log"))
pylines = grep(wwwlog,"python")
for line in pylines:
    print line,

生成器的微妙之处在于,它经常和其他可迭代的对象(如列表或文件)混合在一起。特别是在编写如for item in s这样的语句时,s可以代表一个列表、文件的各行、生成器函数的结果,或者支持迭代的其他任何对象。能够在s中插入不同对象,为创建可扩展的程序提供了一个强大的工具。

通常,函数运行时要使用单一的一组输入参数。但是,函数也可以编写成一个任务程序, 用来处理发送给它的一系列输入。这类函数被称为协程,它是通过将yield语句作为表达式(yield)的形式创建的,如下所示:

def print_matches(matchtext): 
    print "Looking for",matchtext
    while True:
         line = (yield)    # 获得一行文本
         if matchtext in  line:
             print line

要使用这个函数,首先要调用它,向前执行到第一条(yield)语句,然后使用send()给它发送数据,例如:

>>> matcher = print_matches("python")
>>> matcher.next() # 向前执行到第一条(yield)语句
Looking for python
>>> matcher.send("Hello World")
>>> matcher.send("python is cool")
python is cool
>>> matcher.send("yow!")
>>> matcher.close()    # matcher函数调用结束
>>>

使用send()为协程发送某个值之前,协程会暂时中止。发送值之后,协程中的(yield)表达式将返回这个值,而接下来的语句将会处理它。处理直到遇到下一个(yield)表达式才会结束,这时函数将暂时中止。正如上一个例子所示,这个过程将会继续下去,直到协程函数返回或者调用它的close()方法为止。

基于生产者—消费者模型(即程序的一部分生成的数据会被程序的另一部分使用)编写并发程序时,协程十分有用。在这种模型中,协程代表了数据的一个消费者。下面给出了一起使用生成器和协程的一个例子:

# 一组匹配器协程 
matchers = [
    print_matches("python"),
    print_matches("guido"),
    print_matches("jython")
]
# 通过调用next()准备所有的匹配器
for m in matchers: m.next()

# 将一个活跃的日志文件传递给所有的匹配器。注意,为保证运行正常,
# 必须有一台Web服务器持续将数据写入日志
wwwlog = tail(open("access-log"))
for line in wwwlog:
    for m in matchers:
        m.send(line)        # 将数据发送到每个匹配器协程中

第6章将会进一步介绍协程。

程序中使用的所有值都是对象。对象由内部数据和各种方法组成,这些方法会执行与这些数据相关的各种操作。前面在处理像字符串和列表这样的内置类型时,就已经用到了对象和方法。例如:

items = [37, 42]    # 创建一个列表对象
items.append(73)    # 调用append()方法

dir()函数可以列出对象上的可用方法,是进行交互式实验的有用工具,例如:

>>> items = [37, 42]
>>> dir(items) 
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
...
 'append', 'count', 'extend', 'index', 'insert', 'pop',
 'remove', 'reverse', 'sort']
>>>

查看对象时,会看到诸如append()insert()等很熟悉的方法。但也可以看到固定以双下划线开始和结束的特殊方法。这些方法用于实现各种语言运算。例如,__add__()方法实现了+运算符的功能:

>>> items. __add__([73,101])
[37, 42, 73, 101]
>>>

class语句用于定义新的对象类型,实现面向对象编程。例如,下面的类定义了一个支持push()pop()length()操作的简单栈:

class Stack(object): 
    def __init__(self):           # 初始化栈
        self.stack = [ ]
    def push(self,object):
        self.stack.append(object)
    def pop(self):
        return self.stack.pop()
    def length(self):
        return len(self.stack)

在类定义的第一行中,语句class Stack(object)Stack声明为一个object。使用圆括号是Python指定继承的方式——在这个例子中,Stack继承自objectobject也是所有Python类型的根类型。类定义中使用def语句定义了方法。每个方法中的第一个参数始终指向对象本身。根据约定,该参数的名称为self。涉及对象属性的所有操作都必须显式引用self变量。以双下划线开始和结束的方法是特殊的方法。例如,__init__用于在创建对象后初始化该对象。

要想使用类,可编写如下所示的代码:

s = Stack()           # 创建一个栈
s.push("Dave")        # 在栈中放入一些内容
s.push(42)
s.push([3,4,5])
x = s.pop()         # x的值为[3,4,5]
y = s.pop()         # y的值为42
del s               # 删除s

这个例子创建了一个全新的对象来实现栈。但是,栈与内置的列表对象几乎完全相同。因此,继承list然后添加一个额外的方法也是可行的:

class Stack(list):
    # 为栈接口添加push()方法
    # 注意:列表已经提供了一个pop()方法
    def push(self,object):
        self.append(object)

通常,类中定义的所有方法只适用于该类的实例(即创建的对象)。但是,也可以定义不同种类的方法,如C++和Java程序员所熟知的静态方法,例如:

class EventHanlder(object):
    @staticmethod
    def dispatcherThread():
        while (1):
            # 等待请求
            ...

EventHandler.dispatcherTread()       # 像函数一样调用方法

在这个例子中,@staticmethod将方法声明为静态方法。@staticmethod是使用装饰器(decorator)的一个例子,我们将在第6章中进一步介绍装饰器。

如果程序中出现错误,就会引发异常,并显示类似下面的回溯消息:

Traceback (most recent call last): 
 File "foo.py", line 12, in <module>
IOError: [Errno 2] No such file or directory: 'file.txt'

该回溯消息指出了所发生的错误类型及位置。通常情况下,错误会导致程序终止。但是可以使用tryexcept语句捕捉并处理异常,如下所示:

try:
    f = open("file.txt","r")
except IOError as e:
    print e

如果出现IOError,引发错误的详细信息将被放在对象e中,然后控制权被传递给except代码块中的代码。如果出现其他类型的异常,对象e将被传递给用于处理这些异常的代码块(如果有的话)。如果没有出现错误,except代码块中的代码将被忽略。处理完异常后,程序将继续执行紧跟在最后一个except代码块后面的语句。程序不会返回到发生异常的位置。

raise语句用于手工引发异常。引发异常时,可以使用任意一个内置异常,如下所示:

raise RuntimeError("Computer says no")

你也可以创建自己的异常,这将在5.4.2节中详细介绍。

在进行异常处理时,如何正确地管理系统资源(如锁、文件和网络连接)通常是一个棘手的问题。为了简化此类编程,可以对某些种类的对象使用with语句。下面的例子给出了使用互斥锁的代码:

import threading 
message_lock = threading.Lock()
...
with message_lock:
    messages.add(newmessage)

在这个例子中,with语句执行时会自动获取message_lock对象。当执行离开with代码块上下文后,锁将被自动释放。不管with代码块内部发生了什么,都会出现这种管理行为。例如,如果出现一个异常,当控制离开代码块环境时锁也将被释放。

with语句通常只适用于与系统资源或执行环境相关的对象,如文件、连接和锁。但是,用户定义的对象也可以定义自己的自定义处理机制。这一点将在3.9.11节中详细介绍。

随着程序变得越来越大,为了便于维护,需要把它分为多个文件。为此,Python允许把定义放入一个文件中,然后在其他程序和脚本中将其作为模块导入。要创建模块,可将相关的语句和定义放入与模块同名的文件中(注意,该文件的后缀必须是.py)。例如:

# 文件:    div.py 
def divide(a,b):
    q = a/b       # 如果a和b是整数,则q也是整数
    r = a – q*b
    return (q,r)

要在其他程序中使用该模块,可以使用import语句:

import div
a, b = div.divide(2305, 29)

import语句创建了一个新的命名空间,并在该命名空间中执行与.py文件相关的所有语句。要在导入后访问命名空间的内容,只要使用该模块的名称作为前缀,正如上面例子中的div.divide()一样。

如果要使用不同的名称导入模块,可以给import语句加上可选的as限定符,如下所示:

import div as foo
a,b = foo.divide(2305,29)

要将具体的定义导入到当前的命名空间中,可使用from语句:

from div import divide
a,b = divide(2305,29)      # 不再使用div前缀

要把模块的所有内容加载到当前的命名空间中,还可以使用以下语句:

from div import *

与对象一样,dir()函数可以列出模块的内容,是进行交互式实验的有用工具:

>>> import string
>>> dir(string) 
['__builtins__', '__doc__', '__file__', '__name__', '_idmap',
 '_idmapL', '_lower', '_swapcase', '_upper', 'atof', 'atof_error',
 'atoi', 'atoi_error', 'atol', 'atol_error', 'capitalize',
 'capwords', 'center', 'count', 'digits', 'expandtabs', 'find',
...
>>>

使用Python时,有几个快速获取可用信息的来源。首先,以交互模式运行Python时,可以使用help()命令获得有关内置模块和Python其他方面的信息。单独输入help()将获得一般信息,而输入help('模块名')则可获得具体模块的信息。如果提供函数名称,help()命令还可以返回该函数的详细信息。

大多数Python函数都有描述该函数用途的文档字符串。要打印这个文档字符串,只要打印__doc__属性即可。例如:

>>> print issubclass.__doc__
issubclass(C, B) -> bool

Return whether class C is a subclass (i.e., a derived class) of class B.
When using a tuple as the second argument issubclass(X, (A, B, ...)),
is a shortcut for issubclass(X, A) or issubclass(X, B) or ... (etc.).
>>>

最后但也很重要的一点是,大多数Python安装还包括了命令pydoc。该命令用于返回有关Python模块的文档。只需在系统命令提示符后输入“pydoc 主题”即可。


本章介绍Python程序的词法和语法约定。本章涉及的主题包括行结构、语句分组、保留字、字面量、运算符、标记和源代码编码。

程序中的每条语句都以换行符结束。使用续行符(\)可将长语句分为几行,如下所示:

a = math.cos(3 * (x - n)) + \ 
    math.sin(3 * (y - n))

三引号字符串、列表、元组或字典的定义如果跨越多行,是可以不使用续行符的。一般来说,包含在圆括号(...)、方括号[...]、大括号{...}或三引号中的任意程序部分都可以放在多行上,而不需要使用续行符,因为它们清晰地表示出了定义的开始和结束。

缩进用于表示不同的代码块,如函数、条件语句、循环和类的主体。代码块中首条语句的缩进量可以是任意的,但整个代码块中的缩进必须保持一致,例如:

if a: 
    statement1    # 缩进一致,正确
    statement2
else:
    statement3
      statement4  # 缩进不一致,错误

如果函数、条件语句、循环或类的主体较短,只包含一条语句,就可以将其放在同一行上,例如:

if a:  statement1
else:  statement2

要表示一个空的主体或代码块,可使用pass语句,例如:

if a: 
    pass
else:
    statements

尽管可以用制表符进行缩进,但并不鼓励这样做。Python编程社区的首选是用空格(而且也鼓励你这样做)。遇到制表符时,系统会将其转换为移到下一个为8倍数的列所需的空格量(例如,如果一个制表符出现在第11列中,那么系统会填充足够多的空格以移到第16行)。如果运行Python时使用-t选项,那么在同一程序代码块中发现有制表符和空格混用的情况时,就会显示警告信息。使用-tt选项可将这些警告信息转换为TabError异常。

要在一行上放置多条语句,可以使用分号(;)隔开各条语句。如果一行上只有一条语句,也可以使用分号结尾,但这是不必要的。

#字符表示此行的内容都是注释,但出现在引号字符串中的#号无此作用。

最后,除非是在交互模式下运行,否则解释器将忽略所有空白行。在交互模式下,如果输入一条多行语句,空白行即表示输入结束。

标识符是用来识别变量、函数、类、模块和其他对象的名称。标识符可以包含字母、数字和下划线(_),但必须以非数字字符开始。字母目前只允许使用ISO-Latin字符集中的字符A~Z和a~z。由于标识符是区分大小写的,所以FOOfoo是两个不同的标识符。诸如$%@等的特殊符号不允许出现在标识符中。另外,像ifelsefor这样的单词是保留字,也不能用作标识符名称。下面的列表显示了所有保留字:

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

以下划线开始或结束的标识符通常具有特殊含义。例如,以一个下划线开始的标识符(如_foo)不能使用from module import *语句导入。前后均带有双下划线的标识符(如__init__)是为特殊方法保留的,而只有前面带有双下划线的标识符(如__bar)则用于实现私有的类成员,这一点将在第7章介绍。一般用途的标识符应避免使用以上几种格式。

内置的数字字面量分为4种类型:

标识符TrueFalse会被解释为布尔值,其整数值分别是1和0。像1234这样的数字会被解释为十进制整数。要使用八进制、十六进制或二进制指定整数,可以在值的前面分别加上0o0x0b(如0o6440x100fea80b11101010)。

在Python中,整数的位数是任意的,所以,如果要指定一个非常大的整数,只需写出所有位数,如1234568901234567890。但是在检查值和查看过去的Python代码时,可能会看到后面加上字母l(小写的L)或L字符的大数字,如12345678901234567890L。这个结尾处的L表示Python会根据值的大小,选择将整数内部表示为固定精度的机器整数或任意精度的长整数类型。在老版本的Python中,可以显式地选择使用任一种类型,并且可以在数字结尾加上字母L表示这是长类型。如今,这种区分已经没有必要,不鼓励使用。因此,表示大整数值时不必加上L

123.341.2334e+02这样的数字会被解释为浮点数。整数或浮点数后面加上jJ就构成了虚数,如12.34J。用一个实数加上一个虚数就构成了复数,方法是将实数和虚数加起来,如1.2+12.34J

字符串字面量用于指定一个字符序列,其定义方法是把文本放入单引号(')、双引号(")或三引号('''""")中。这三种引号形式在语义上没有差别,但要求在字符串开始和结尾使用的引号类型必须相同。置于单引号和双引号中的字符串必须定义在一行上,而三引号的字符串可以分布在多行上,并且会将所有格式符号(即换行符、制表符、空格等)包含在内。像"hello" 'world'这样的相邻字符串(由空格、换行符或续行符隔开)将被连接起来,形成一个字符串"helloworld"。

在字符串字面量中,反斜杠(\)字符用于转义特殊字符,如换行符、反斜杠本身、引号和非打印字符。表2-1列出了可识别的一些转义码。无法识别的转义符序列将保持原样,包括最前面的反斜杠在内。

表2-1 标准的字符转义码

字  符

描  述

\

续行符

\\

反斜杠

\'

单引号

\"

双引号

\a

Bell(音箱发出提示音)

\b

退格符

\e

Escape

\0

Null(空值)

\n

换行符

\v

垂直制表符

\t

水平制表符

\r

回车符

\f

换页符

\ooo

八进制值(\000~\377

\uxxxx

Unicode字符(\u0000~\uffff

\Uxxxxxxxx

Unicode字符(\U00000000~\Uffffffff

\N{字符名称}

Unicode字符名称

\xhh

十六进制值(x00~xff

转义码\OOO\x用于将字符嵌入到很难输入的字符串字面量(如控制码、非打印字符、符号、国际字符等)中。对于这些转义码,你必须指定对应于字符值的整数值。例如,若要输入单词Jalapeño的字符串字面量,可以输入"Jalape\xf1o",其中的\xf1就是ñ的字符代码。

在Python 2中,字符串字面量对应于8位字符或面向字节的数据。这种字符串有一个很严重的缺陷,即它们无法完全支持国际字符集和Unicode。为了解决这个问题,Python 2对Unicode数据使用了单独的字符串类型。要输入Unicode字符串字面量,应在第一个引号前加上前缀"u",例如:

s = u"Jalape\u00f1o"

在Python 3中不必加这个前缀字符(而且如果加上会算作语法错误),因为所有字符串已经使用了Unicode编码。如果使用-U选项运行解释器,Python 2将会模拟这种行为。(即所有字符串字面量将被作为Unicode字符对待,u前缀可以省略。)

无论你使用哪个Python版本,表2-1中的\u\U\N转义码都可用于在Unicode字面量中插入任意字符。每个Unicode字符都有一个指定的码点(code point),在Unicode字符集中一般表示为U+XXXX,其中XXXX是由4个或更多个十六进制数字表示的序列。(注意,这种表示法并非Python语法,但作者们在描述Unicode字符时经常使用它。)例如,字符ñ的码点是U+00F1\u转义码用于插入码点范围在U+0000U+FFFF之间的Unicode字符(如\u00f1)。\U转义码用于插入码点范围在U+10000及以上的字符(如\U00012345)。使用\U转义码时请注意,码点在U+10000以上的Unicode字符通常被分解为一对字符,称为代理编码对(surrogate pair)。这与Unicode字符串的内部表示有关,第3章将会对此进行更详细的介绍。

Unicode字符还有一个描述性名称。如果知道名称,就可以使用\N{字符名称}转义序列,例如:

s = u"Jalape\N{LATIN SMALL LETTER N WITH TILDE}o"

关于码点和字符名称的权威性参考,请参阅http://www.unicode.org/charts

另外,可以在字符串字面量前面加上rR,如r'\d'。这些字符串称为原始字符串,因为其中所有的转义字符都会原封不动地保留,也就是说,这种字符串包含的文本只表示其字面上的含义,包括反斜杠在内。原始字符串的主要用途是指定其中反斜杠字符是有实际含义的字面量。例如,指定配合re模块一起使用的正则表达式,或者Windows计算机上的一个文件名(如r'c:\newdata \tests')。

原始字符串不能以单个反斜杠结尾,如r"\"。在原始字符串中,如果前面\字符的数量是奇数个,\uXXXX转义序列仍然会解释为Unicode字符。例如,ur"\u1234"定义的是包含单个字符U+1234的原始Unicode字符串,而ur"\\u1234"定义的则是包含7个字符的字符串,其中前两个字符是反斜杠,余下5个字符是字面量"u1234"。此外,如上所示,在Python 2.2中,r必须出现在原始Unicode字符串中的u之后。在Python 3.0中,u前缀是可选的。

不能使用对应于UTF-8或UTF-16等数据编码的原始字节序列来定义字符串字面量。例如,直接输入像'Jalape\xc3\xblo'这样的原始UTF-8编码字符串,将会产生一个9个字符的字符串U+004A、U+0061、U+006C、U+0061、U+0070、U+0065、U+00C3、U+00B1、U+006F,这可能不是你想要的结果。因为在UTF-8中,多字节序列\xc3\xb1代表一个字符U+00F1,而不是两个字符U+00C3和U+00B1。要将一个已编码的字节字符串指定为字面量,在第一个引号前加上"b",即b"Jalape\xc3\xblo"。这样才能从字面上创建一个单字节的字符串。我们可以使用decode()方法解码字节字面量的值,将这种表示法的字节字面量转换为一个普通的字符串。关于这方面的更多细节,将在第3章和第4章中介绍。

字节字面量在大多数程序中都极少使用,因为这种语法直到Python 2.6才出现,而且在该版本中,字节字面量和普通字符串之间没有差别。但在Python 3中,字节字面量变成了与普通字符串不同的新的bytes数据类型(参见附录A)。

将一些值放在方括号[...]、圆括号(...)和花括号{...}内,可分别表示一个列表、元组和字典中包含的对象集合,如下所示:

a = [ 1, 3.4, 'hello' ]        # 一个列表
b = ( 10, 20, 30 )             # 一个元组
c = { 'a': 3, 'b': 42 }        # 一个字典

列表、元组和字典字面量可以在不使用续行符(\)的情况下分布在多行上。另外,最后一项后面允许跟一个逗号,例如:

a = [ 1, 
      3.4,
      'hello',
    ]

Python可以识别的运算符有:

+    -   *   **   /   //   %    <<   >>     &     |
^    ~    <   >    <=  >=   ==  !=   <>     +=
-=   *=  /=  //=  %=  **=  &=  |=   ^=     >>=  <<=

以下标记可以用作表达式、列表、字典和语句不同部分的分隔符:

(  )  [  ]  {  }   ,   :   .   `   =   ;

例如,等号(=)在赋值语句中可用作名称和值之间的分隔符,而逗号(,)则可用于分隔函数的各个参数,列表和元组中的各个元素等。小数点(.)可用在浮点数中以及在扩展切片操作中组成省略号(...)。

最后,语句中也会用到下面这些特殊符号:

'   "   #   \   @

字符$?在Python中没有任何意义,不能出现在程序中,但可以出现在带引号的字符串字面量中。

如果模块、类或函数定义的第一条语句是一个字符串,该字符串就成为了相关对象的文档字符串,如下所示:

def fact(n): 
    "This function computes a factorial"
    if (n <= 1): return 1
    else: return n * fact(n - 1)

代码浏览工具和文档生成工具有时会用到文档字符串。通过对象的__doc__属性可以访问文档字符串,如下所示:

>>> print fact.__doc__
This function computes a factorial
>>>

文档字符串的缩进必须与定义中的所有其他语句保持一致。另外,文档字符串不能通过表达式进行计算或者通过变量进行赋值。文档字符串必须是包含在引号中的字符串字面量。

函数、方法或类定义的前面可以使用一个特殊的符号,称为装饰器,其目的是修改后面定义的行为。装饰器使用@符号表示,必须单独放在对应的函数、方法或类之前的那行上,例如:

class Foo(object):
   @staticmethod 
   def bar():
       pass

可以使用多个装饰器,但每个装饰器必须各占一行,例如:

@foo
@bar
def spam():
    pass

第6章和第7章中将介绍有关装饰器的更多内容。

编写Python源程序时一般使用标准的7位ASCII码。但是,在Unicode环境中工作的用户可能会发现这很别扭——特别是当他们必须使用国际字符编写大量字符串字面量时。

只需在Python程序的第1行或第2行中包含一个特殊的编码注释,就可以使用不同的编码编写Python源代码:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

s = "Jalapeño"   # 引号中的字符串直接使用UTF-8编码。

程序中包含特殊的coding:注释语句时,可以直接使用支持Unicode的编辑器输入字符串字面量。但是,Python的其他元素(包括标识符名称和保留字)还是只能使用ASCII字符。


相关图书

Python极客项目编程(第2版)
Python极客项目编程(第2版)
动手学自然语言处理
动手学自然语言处理
Python财务应用编程
Python财务应用编程
Web应用安全
Web应用安全
深度学习的数学——使用Python语言
深度学习的数学——使用Python语言
Python量子计算实践:基于Qiskit和IBM Quantum Experience平台
Python量子计算实践:基于Qiskit和IBM Quantum Experience平台

相关文章

相关课程