第一章: 未整理
Posted amou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一章: 未整理相关的知识,希望对你有一定的参考价值。
i辨婉骑银雜;鍵綠
Zi...
Python简介
j章快速介绍Python这门语言,目标是在阐明Python的大部分基本特性的同时,又不会太过纠 +缠于特殊的规则或细节。为此,本章简要讲述一些基本概念,如变量、表达式、控制流、 函数、生成器、类和输入/输出。本章不追求大而全,但有经验的程序员应该能够从本章中的资料推而 广之,创建出更加髙级的程序。初学者应该多尝试一些实例,才能对这门语言有一定的了解。如果你 对Python不熟悉也没有使用过Python 3,可以使用Python 2.6来学习本章内容。实际上,本章介绍的主 要槪念同时适用于这两个版本,但在Python 3中存在极少数可能与本章中给出的例子不符的关键语法 变化,其中大多数与打印和I/O有关。请参考附录A,以了解详细信息。
1.1 运行 Python
Pythong序是由解释器来执行的。通常,只要在命令shell中输人python即可启动解释器。然而, 解释器和Python开发环境存在多种实现(例如Jython、IronPython, IDLE、ActivePython、Wing IDE、 pydev等),因此需要参考相应文档中的启动说明。解释器启动后将出现一个命令提示,在此可以开始 输入程序,进入简单的读入-求值循环。例如,在下面的输出中,解释器显示了版权消息和》>提示符, 用户可以在这里输入熟悉的打印Wello 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",
>? print "Hello World‘*
Hello World >?
credits
or
license" for
more
information.
注意如果在尝试前述例子时出现了语法错误,你使用的很可能就是Python3。遇到这种情况并不妨 碍你继续学习本章的内容,但要知道打印语句在Python3中已经变为一个函数。在下面的例子 中,只要在要打印的内容两边加上括号即可正常运行,例如:
?>
print(uHello World") Hello World >?
如果要打印的内容只有一项,在要打印内容两边放置括号的方法在Python2中同样有效。然而, 这种语法在现有的Python代码中并不常见。在后面的章节中,这种语法有时会用在与打印无直 接关系的展示特性的例子中,但这些例子应该同时适用于Python2和3。
www.TopSage.com
1.2变量和算术表达式 3
Python的交互模式是它最有用的功能之一。在交互式shell中,可以输入任意合法的语句或语句序 列,然后立即査看结果。很多人甚至使用交互式Python作为桌面计算器,作者本人也如此。例如:
?>
>? 6000 + 4523,50 + 134.12
10657.620000000001 + 8192.32
18849.940000000002
?>
以交互方式使用Python时,特殊变量_用于保存最后一次运算的结果。如果要在后续语句中保存 或使用最后一次运算的结果,使用此变量十分方便。但要强调一点,此变量只有在以交互方式工作时 才有定义。
如果要创建可以重复运行的程序,可将语句放到一个文件中:
# helloworld.py print "Hello World
Python源文件是普通的文本文件,后缀通常是.pyD #字符表示整个一行都是注释。 要执行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。程序可以通过抛出SystemExit异常来请求退出。
?>
raise SystemExit
1.2变量和算术表达式
程序清单l-i中的程序通过执行一次简单的复利计算,说明变量和表达式的用法
#初始金额 #利率 #年数
principal = 1000 rate = 0.05 numyears - 5 year = 1
while year <= numyears:
principal = principal ? (1 + rate) print year, principal
year += 1
# 注意:在Python 3中是print (year, principal)
www.TopSage.com
第1章Python简介
此程序的输出如下所示:
1 1050.0
2 1102.5
3 1157.625
4 1215-50625
5 1276.2815625
Python是一种动态类型的语言,在程序执行过程中,可将变量名称绑定到不同的值,而且这些值 可以属于不同的类型。赋值运算符的作用仅仅是在名称和值之间创建一种关联。尽管每个值都有一个 相关类型,如integer或string,但变量名称是无类型的,在执行过程中可以引用任意类型的数据。 这与C语言不同,例如在C语言中,名称代表了用于保存值的固定类型、大小和内存位置。Python的动 态行为可以从程序清单14的principal变量看出来。最初给它分配的是一个integer?,但程序稍后 给它重新赋了值,如下所示:
principal = principal * (1
rate)
这条语句对表达式求值,并把名称principal重新与结果关联。principal的原始值是整数类型 的1000,但现在的新值是浮点数(rate被定义为浮点数,因此上述表达式的值也是浮点数)。因此在 程序中,principal的显式类型从integer动态变为了float。然而准确地说,不是principal的类型 变了,而是principal名称引用的值的类型变了。
换行代表一条语句的结束。然而,也可以在同一行上使用分号来隔开多条语句,如下所示:
principal = 1000; rate = 0.05; numyears
5;
while语句对随后的条件表达式进行测试。如果被测试的语句为True,while语句的主体就会执 行。然后再次测试条件,再执行主体,直到条件变为False。因为循环主体是由缩进表示的,每次循 环时都会执行程序清单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
格式化字符串包含普通文本和特殊的格式化字符序列,如”%十、和《%f。。这些序列分别用 于指定特定类型数据的格式,如整数、字符串或浮点数。特殊的字符序列还可以包含用于指定宽度和 精度的修饰符。例如,u%3d”将一个整数格式化为在一个宽度为3的列中右对齐,而40.2f”将一个浮 点数格式化为在小数点后只出现两位数字。格式化字符串的行为与C语言中的printf ()函数几乎完全 相同,第4章将对此进行详细说明。
更新的字符串格式化的方法是使用forint (》函数单独格式化每个部分。例如:
print format(year,u3d"),format(principal,"0.2f")
print (format (year, u3d") , format (principal, ?‘0.2f")) # Python 3
www.TopSage.com
1.3条件语句
()函数使用的格式说明符类似于传统字符串格式化运算符(%)使用的格式说明符。例如, ??3d”将一个整数格式化为在一个宽度为3的列中右对齐,而“.2f ?,将一个浮点数格式化为两位精度。 字符串还有一个format (>方法,可用于一次性格式化很多值。例如:
print "{0:3d} {1: 0.2f } n . format (year,principal)
print("{0:3d} {1:0.2f}".format(year,principal)) # Python 3
在这个例子中,"{0: 3dp和n {1:0.2f P中冒号前的数字代表传递给format ()方法的相关参数, 而冒号后的部分则是格式说明符。
1.3条件语句
if与else语句可执行简单的测试,如下所示:
if a < b:
print "Computer says Yes"
else:
print MComputer says No
if和else子句的主体是用缩进表示的。else子句是可选的。 要创建一条空子句,可以使用pass语句,如下所示:
if a < b: pass
# Do nothing
else:
print "Computer says No
使用or、and^Onot关键字可以建立布尔类型的表达式:
if product == "gamen and type == "pirate memory11 \ and not (age < 4 or age
8):
print ?‘工*11 take it!
注意编写复杂的测试用例通常需要编写很长的代码行,看起来令人生厌。为了提高代码的可读性, 可以像上面一样在一行的結尾使用反斜杠⑴,然后就可以在下一行继续书写上一条语句的内 容。如果这样做,正常的缩进规则不适用于下一行,因此可以随意设置后续行的格式。
Python没有专门的switch或case语句用于测试多个值,要处理多个测试用例,可以使用elif语 句,如下所示:
if suffix == ".htmH:
"text/html*‘
elif suffix == *‘. jpg°:
content = "image/jpeg
elif suffix == u.pngM:
content = uimage/png"
content
else:
raise RuntimeError {"Unknown content type *‘)
要表示真值,可以使用布尔值Tru6和False,例如:
if 1 sparu‘ in s:
has_spam = True
else:
has—spam = False
www.TopSage.com
6 第1章Python简介
所有关系运算符(如<和>)的结果都返回Ture或False。本例中使用的in运算符通常用于检査 某个值是否包含在另一个对象(字符串、列表或字典)中。它也返回Ture或False,因此前一个例 子可以缩短为:
has_spam = ‘spam1 in s
1.4文件输入和输出
以下程序可打开一个文件并逐行读取该文件的内容:
#返因一个文件对象 #调用文件的readline ()方法
f = open("foo.txtn} line = f.readline(} while line:
print line,
# print(line,end=11) line = f.readline() f.close()
#后面跟、?将忽略换行符 #在1>7*0113中使用
openG函数返回一个新的文件对象。调用该对象上的方法可以执行各种文件操作。readline () 方法读取一行内容,包括结尾的换行符在内。读至文件结尾时将返回空字符串。
在这个例子中,程序只是循环读取了文件foo.txt中的所有行。如果程序在像这样的数据集(例 如输入中的行、数字、字符串等)上进行循环,那么通常就称为迭代。因为迭代是很常见的一种操作, 所以Python为其提供了一条专用语句for,用于迭代内容项。例如,同样的程序可以写成下面这种更 简洁的形式:
for line in openpfoo.txt”): print line,
要将程序的输出送到一个文件中,需要在print语句后面使用>>指定一个文件,如下所示: #打开文件以便写入
f = open ("out", "W ) while year
principal = principal * (1 + rate) print ?f, n%3d %0.2fa % (year,principal) year += 1
numyears:
f,close()
>>语法只能用在Python2中。如果使用PythOn3,可将print语句改为以下内容: print ("%3<3 %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 :")
www.TopSage.com
1.5字符串
在Python 3中,raw_input ()函数叫做input <),但它们的工作方式完全相同。
1.5字符串
要创建一个字符串字面量,将字符串放在单引号、双引号或三引号中即可,如下所示:
"Hello World"
‘Python is groovy‘
"""Computer says ‘No
字符串前后使用的引号必须是对应匹配的。两个三引号之间出现的所有文本都视为字符串的内 容,而使用单引号和双引号指定的字符串必须在一个逻辑行上。当字符串字面量的内容需放在多个文 本行上时,三引号字符串就很有用,如下所示:
print ‘‘‘Content-type: text/html
<hl> Hello World </hl>
Click <a href="http://www.python.org">here</a>.
字符串存储在一个以0开始、使用整数索引的字符序列中。要提取其中的一个字符,可以使用索 引运算符SU],如下所示:
b = a[4]
Hello World"
要提取一个子字符串,可以使用切片运算符S U d]。这会提取字符串s中索引位置k处的所有字符,
其中索引Jc的范围是i<=k<:/。如果省略i,则假定使用字符串的起始位置,如果省略则假定使用字
符串的结尾位置:
c = at:5] d = a[6:] e = a [3 : 8]
可以使用加(+)运算符连接两个字符串:
Hello"
# d = "World"
"lo Wo"
This is a test
Python绝不会把字符串的值解释为数值数据(像Pei域php等语言中会这样解释)。例如,+运算符 始终会连接字符串:
113 7 11 "42"
3742"(字符串连接)
要执行数学计算,首先要使用像inU)或float ()函数将字符串转换为数值,例如:
=int(x) + int(y)
79 (Integer +)
使用std》、reprO或format ()函数可将非字符串值转换为字符串表示形式,例如:
"The value of x is "The value of x is
+ str(x)
+ repr(x)
"The value of x is " + format(x,"4d")
尽管str()和reprU都可以创建字符串,但它们的输出通常存在细微的差别。str()生成的输出 与使用print语句得到的输出相同,而用repr()创建的字符串可表示程序中某个对轰的精确值,柄如:
?> x
3.4
?>
‘3.4‘
Btr(x)
www.TopSage.com
8 第1章Python简介
?> repr(x)
■3.3999999999999999‘
>?
上例中3.4的不精确表示并非是Python中的一个bug。这是双精度浮点数的一个特点,因为从设计 上说,底层计算机硬件无法精确地表示十进制小数。
format ()函数的作用是利用特定格式将值转换成字符串,例如:
>?
format(x,"0.5fn)
?3.40000‘
?>
names
["Dave",
Mark", "Ann",
4
a
b
a
g
x
Y
F灘玉::
後€
A7/
+
奚忘;A
5
7
a +
x + y
# z
# b
# C
#
# z
a = names[2] names[0] = "Jeff"
1.6列表
列表是任意对象的序列。把值故入方括号中就可以创建列表;如下所示: Phil-]
列表使用从0开始的整数索引,使用索引运算符可以访问并修改列表中的项:
#返回列表的第3项1■Ann”
#将第1项改为-Jeff-
要将新项追加到列表末尾,可使用append ()方法:
names.append("Paula")
要将一项插入到列表中,可使用insert ()方法:
names
.insert(2, "Thomas")
使用切片运算符可以提取一个子列表或对子列表重新赋值:
b = names[0:2] c = names[2:]
names[1J = names[0:2]
# 返回["Jeff", "Mark"]
# 返回["Thomas", "Ann", "Phil", "Paula"] #将!131!^3中的第2项替换为‘Jeff‘
[‘Dave‘ , ‘Mark?, 1 Jeff U #将列表的头两项替换为右边的列表
# the list with the list on the right.
Jeff‘
# 结果是[1,2,3,4,5]
names
names
[]
使用加(+ )运算符可以连接列表:
[1,2,3] + [4,5]
创建一个空列表有两种方式:
# 一个空列表
# 一个空列表
列表可以包含任意种类的Python对象,包括其他列表在内,如下例所示: [1,"Dave",3.14, [”Mark", 7, 9, [100,101]], 103
嵌套列表中包含的项需要使用多次索引运算才能访问到,例如:
# _退回"
#返因9
#返因101
a[l]
Dave
a[3] [2] a[3][3][1]
程序清单1-2中的程序展示了列表的一些髙级特性,该程序会读取在命令行上指定的一个文件中 的数值列表,然后输出其中的最大值和最小值。
a
a
=list()
import sys
if len(sys.argv) != 2
print "Please supply
filename"
#加载sys模块 #检査命令行参数的数量:
a
www.TopSage.com
1.7元组 9
raise SystemExit(1) f = open(sys.argv[1]) lines = f.readlines{) f .closed
#命令行上的文件名 #将所有行读取到一个列表中
#将所有输入值从字符串转换为浮点数
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()计算出最大值和最小值即可。
1-7元组
<
要创建简单的数据结构,可以使用元组将一组值打包到一个对象中。在圆括号中放入一组值即可 创建元组,例如:
stock = {‘GOOG‘, 100, 490.10) address
person = (first_name, last_name, phone)
(■www.python.org‘, 80)
即使没有圆括号,Python通常也能识别出元组:
stock = ‘GOOG?, 100, 490.10 address person
‘www.python.org1,80 first_name, last_name, phone
为了完整起见,也可以定义o个和1个元素的元组,但语法较为特殊:
# 0-元组(空元组)
# 1-元组(注意随后的逗号)
# 1-元组(注意随后的逗号)
和列表一样,也可以使用数字索引来提取元组中的值。然而,更常见的做法是将元组解包为一组 变量,例如: ,
a
0
b = {item,) c = item.
name, shares, price host, port = address first_name, last_name, phone
stock
person
尽管元组支持的大部分操作与列表的相同(如索引、切片和连接),但创建元组后不能修改它的 内容(即无法替换、删除现有元组中的元素或插入新元素)。这说明最好把元组看成一个由多个部分 组成的对象,而不是可在其中插入或删除项的不同对象的集合。
www.TopSage.com
10
第1章Python简介
因为元组与列表之间存在诸多相似之处,所以有些程序员往往完全忽略了元组,而只使用列表, 因为后者看似更灵活。尽管这并无不可,但如果程序要创建大量的小列表(即包含的项少于12个), 就会造成内存浪费。这是因为系统会为列表分配稍微多一些内存,以优化添加新项时的操作性能。而 由于元组是不可变的,所以它们的表示更为紧凑,不会占据额外的内存空间。
表示数据时通常同时使用元组和列表。例如,下面的程序显示了如何读取包含不同数据列,且各 数据列由逗号隔开的文件:
#文件中各行的格式为 "name,shares,price filename = "portfolio.csv° portfolio =[]
for line in open(filename): fields = 1 ine. split (",11)
=fields[0]
name
shares = int(fields[1]) price = float(fields[2]) stock
portfolio.append(stock)
(name,shares,price)
#将每行划分为一个列表 #提取并转换每个字段
# 创建一个元组(name, shares, price)
#将记录追加到列表中
字符串的split ()方法会按照指定的分隔符将一个字符串划分为一个字段列表。该程序最后创建 的portfolio^据结构好比一个二维的行列数组,每行由一个元组表示,可通过如下方式访问:
>? portfolio[0]
(‘GOOG*, 100, 490.10) ?> portfolio [1]
(-MSFr1, 50, 54.23)
>>>
毎个数据项可以通过如下方式访问
>?
portfolio[1][1]
50
?> portfolioll] [2]
54.23
>?
下面给出了一种循环所有记录并将字段展开到一组变量中的简单方法:
0.0
total
for name, shares, price in portfolio: total += shares * price
1.8集合
集合用于包含一组无序的对象。要创建集合,可使用set()函数并像下面这样提供一系列的项:
#创建一个数值集合 #创建一个唯一字符的集合
与列表和元组不同,集合是无序的,也无法通过数字进行索引。此外,集合中的元素不能重复。 例如,如果检査前面代码中t集合的值,结果会是:
s = set([3,5,9,10]) set("Hello")
?> t
set([‘H‘, ‘e‘, , ‘o‘])
注意只出现了一个‘I1。
集合支持一系列标准操作,包括并集、交集、差集和对称差集,例如:
www.TopSage.com
# t和s的并集
# t和S的交集 #求差集(项在t中,但不在S中)
#对称差集(项在t或S中,但不会同时出现在二者中)
使用add ()和update ()可以在集合中添加新项:
t.add(‘x‘)
s. updated【10,37,42]} #在s中添加多项
使用removeO可以删除一项:
t. removeCH‘)
1.9字典
字典就是一个关联数组或散列表,其中包含通过关键字索引的对象,在大括号(< >)中放人值 即可创建字典,如下所示:
stock
■GOOG", "shares" : 100, "prica"
490.10
要访问字典成员,可使用关键字索引运算符,如下所示,
stockCname"!
name
value = stock[‘shares"] * shares["price‘]
插人或修改对象的方法是:
stock["sharesB] = 75 stock[ndateD]
"June 7, 2007u
尽管字符串是最常用的关键字类型,但还可以使用其他的Python对象,包括数值和元组。但包括 列表和字典在内的有些对象不能用作关键字,因为它们的内容可以发生变化。
如前所述,在定义一个可包含多个命名字段的对象时,字典是一种很有用的方式。然而,字典也 可用作快速査找无序数据的一个容器。例如,下面是一个股票价格的字典:
prices
■GOOG" : 490.10, "AAPL- : 123.50, -IBM-
■KSFT" : 52.13
:91.50,
创建一个空字典有两种方式 prices = {} prices = dictO # —个空字典
使用in运算符可以测试某个内容项是不是字典成员’如下所示
# ―个空字典
if "SCOX" in prices:
else:
pi prices ( "SCOX"]
p = 0.0
这个特殊的步骤序列还可以写成更简洁的形式,如下所示 p = prices.get {"SCOXu, 0.0)
www.TopSage.com
12
第1章Python简介
t
a
t I s b = t & s
s
)
s
S
要获得一个字典关键字的列表,将字典转换为列表即可:
syms = list(prices)
# syms = [°AAPL", "MSFT",
IBM",
GOOG"]
使用del语句可以删除字典的元素:
del prices.[°MSFT‘‘] ■
字典是Python解释器中最完善的数据类型。因此/如果只是要在程序中存储和处理数据,使用字 典比使用一些自定义数据结构要好得多。
1.10迭代与循环
最常用的循环结构是用于迭代多个项的for语旬。迭代是Python最重要的功能之一。但最常见迭 代形式只是循环一个序列(如字符串,列表或元组)的所有成员,例如:
for n in [1,2,3,4,5,6,7,8,9]:
print "2 to the power is %d" % (n, 2**n)
在这个例子中,毎次迭代都会将列表[1,2,3,4 9]中的下一个值赋给变量n。因为在整数范
围内执行循坏十分常见,为此经常会使用下面的快捷方法:
for n in rangG(l,10):
print "2 to the power is %d" % (n, 2**n)
ranged, j,[,步进值])函数创建的对象表示值i到j‘-l的整数。如果起始值被省略,则认为是0 第三个参数是可选的步进值。例如:
a = range£5) b = range(1,8) c = range(0,14,3) d = range(8,1,-1)
0,1,2,3,4 1,2,3,4,5,6,7 0,3,6,9,12 8,7,6,5,4,3,2
在使用range ()函数时请注意,在Python 2中,它创建的值是已经用整数值完全填满的列表。当 范围非常大时,这可能会在不经意间耗掉所有可用内存。因此,在老式的Python代码中,可能会看到 程序员使用另一个函数xrangeO,例如:
for i in xrange(100000000): statements
# i = 0,1,2,...,99999999
进行査找时,uangeU函数创建的对象会査询时根据需要i十算它所表示的值。为此,它成为了表 示极大范围整数值的首选方式。在Python 3中,xrangel)函数已经更名为ranged,并且已删除了老 式range I)函数的功能。
for语句并不仅限于处理整数序列,还可用于迭代多种对象,包括字符串、列表、字典和文件,
例如:
Hellc world"
#打印出a中的每个字符 for c in a: print c
["Dave", "Mark",-Ann", "Phil"]
#打印出一个列表的成员
for name in b: print
name
{ ‘GOOG1 : 490.10, ‘IBM1 : 91.50, ‘AAPL‘ : 123.15 )
www.TopSage.com
1.11函数 13
?打印出二个字典的所有成员 for key in c:
print key, c[key]
#打印一个文件中的所有行
f = open("foo.txt"J for line in f:
print line.
■循环是Python最强大的语言功能之一,因为你可以创建自定义的迭代对象和生成器函数并给 它提供值序列。本章稍后和第6章将会讲述有关迭代器和生成器的更多内容。
1.11函数
使用语句可以创建函数,如下例所示:
def remainder(a,b): q = a // b r = a - q*b return r
?//是截断除法运算符
要调用函数,只要使用函数名加上用0括号括起来的参数即可,如result = remainder(37, 15)。 如果要让函数返回多个值,可以使用元组,如下所示:
def divide(a,b): q = a // b r - a - q*b return (q,r)
*如果a和b是整数,g就是?■数
使用元迫返回多个值时,可以很容易地将结果放到单独的变量中,例如
quotient, remainder = divide(1456,33)
要给函数参数提供一个默认值,可使用以下赋值方式:
def connect(hostname,port,timeout-300): #函数体
在函数定义中给一个参数提供默认值以后,调用此函数时就可以省略该参数,此时该参数将使用 默认值, 如下所示:
connect(‘www.python.org■, 80)
还可以使用关键字参数调用函数,此时可以按任意顺序提供参数,但这需要你知道函故定义中的 参数名称,如下所示:
connect(part=80,hostname="www.python,org"J
在函数中创建变量或给变s赋值时,该变傲的作用域是局部的,也就是说,该变量只定义在函数 体内部,而且当函数返回值后会立即梢毁该变S,要在函数内部修改某个全局变量的值,可以使用 global语句,如下所示:
count - 0
def foo():
global count count += 1
#更改全局变tcount
www.TopSage.com
14 第1章Python简介
1.12生成器
如果使用yield语句,可以让函数生成一个结果序列,而不仅仅是一个值,例如:
def countdown(n):
print "Counting down!" while n > 0:
yield n n -= 1
?生成一个值(n)
任何使用yield的函数都称为生成g。调用生成器函数将创建一个对象,该对象通过连续调用 next()方法(在1^0113中是_1^1:_|))生成结果序列,例如:
>? o = countdown{5) ?> c. next (}
Counting down!
?> c.nsxtO
?> c.
?>
next I)调用使生成器函数一直运行到下一条yield语句为止,此时next ()将返回值传递给 yield,而且函数将蒈时中止执行?再次调用nexto时,函数将继续执行yields后的语句。此过程 持续到函数返回为止,
通常不会像上面这样手动调用next U ,而是会使用一个for循坏,例如:
>? for i in countdown{5) i … print i,
counting down!
5 4 3 2 1
>>>
生成器是基于处理管道.流或数据流编写程序的一种极其强大的方式。例如,下面的生成器函数 模拟了常用于监控日志文件的UNIX tail -£命令的行为:
# tail—个文件(如tail -f) import time de£ tail(£):
£.seek(0,2) while True:
line = f.readline() if not line:
time.sleeptO.1) continue yield line
*移动到EOF
#尝试读取一个新的文本行 *如果没有内容,暫时休眠并再次尝试
下面的生成器用于在很多行中査找特定的子字符串
def grep(lines, searchtext): for line in lines:
if searchtext in line: yield line
下面的例子将以上两个生成器合并在一起,创建了一个简单的处理管道:
# UNIX "tail -f i grep python"命令的python实观 wwwlog = tail(open{"access-log")}
www.TopSage.com
1.13协程 15
pylines = grep(wwwlog,"python") for line in pylines:
print line,
生成器的微妙之处在于,它经常和其他可迭代的对象(如列表或文件)混合在一起,特别是在编 写如for item in s这样的语句时,s可以表示项目的列表,文件的各行,生成器函数的结果,或者 可支持迭代的其他任何对象。能够在s中插人不同对象,这在创建可扩展的程序时可以发挥巨大的作
1-13协程
通常,函数运行时要使用一组输入参数。但是,也可以把函数编写为一个任务,从而能处理发送 给它的一系列输人。这类函数称为协程,可使用yield语句并以表达式(yield)的形式创建协程,如 下所示:
def print_matches(matchtext):
print "Looking for".matchtext while True:
line - (yield) if matchtext in print line
#获得一行文本
line:
要使用这个函数,首先要调用它,向前执行到第一条(yield)语句,然后使用send(>给它发送数 据,例如:
?> matcher = print_matches ("python") ?> matcher.next ()
Looking for python
matcher.send("Hello world")
?> matcher.aendCpython is cool") python is cool ?> matcher, send(uyowl ")
>? matcber ■ close ()
?>
#向前执行到第一条(yield)iS■句
?>
#匹》器函数调用结束
使用send。为协程发送某个值之前,协程会暂时中止。此时,协程中的(yield)表达式将返回这 个值,而接下来的语旬将会处理它?处理直到遇到下一个(yield)表达式才会结束,也就是函数暂时 中止的地方。正如上一个例子所示,这个过程将会继续下去,直到协程函数返回或者调用它的close() 方法为止,
基于生产者-ffi用者模型(即一部分程序生成的数据会被程序的另一部分使用)编写并发程序时, 协隹十分有用。在这种模型中,协程代表了数据的一个使用者,下面给出了共同使用生成器和协程的 一个例子:
# 一组匹*器协钱
matchers
print_matches ("python"), print_matches {"guido"), print_matches {" jytlion")
# a
# b
# d
a
b
5
4
3
用
]
#通过调用next I)准备所有的匹K■器
for m in matchers: m.next ()
www.TopSage.com
16 第1章Python简介
?为所有匹配器提供一个活动的曰忠文件,为此 #必须有一台活动的web服务器将数据写入a*
wwwlog = tail(open{"access-log")) for line in wwwlog:
for m in matchers: m.send(line)
#将数据发送到每个匹a器协程中
第6章将会进一步介绍协程,
1.14对象与类
程序中使用的所有值都是对象,对象由内部数据和各种方法组成,这些方法会执行与这些数据相
关的各种操作。前面在处理像字符串和列表这样的内置类型时,就已经用到了对象和方法。例如:
#创建一个列表对象 #调用append ()方法
dir()函数可以列出对象上的可用方法,是进行交互式试验的有用工具,例如:
items = E37, 42) items.append(73)
?>
items ? [37, 42]
?>
air(itenio)
[*?ad?3?‘, ,—class一,,
__contains_‘, ‘_delattr_1, ?_delitem__ ■,
■append,, ‘count,, .extend., 1 index1, ■insert,,‘pop‘, ■remove‘, ‘reverse1, ‘aart‘]
?>
査看对象的可用方法时,会看到诸如append)和insert!)等很熟悉的方法。但也可以看到始终 以双下划线开始和结束的特殊方法。这些方法用于实现各种语言的运算方式。例如,方法 实现了+运算符的功能:
?> items.__add__([73,101])
[37, 42, 73, 101]
L
>?
在面向对象的编程中,claSSg句用于定义新的对象类型。例如,下面的类定义了带有push pop。和length ()操作的简单栈:
class Stack(object): def __init—(self):
self.stack =[] def pushtself.object)i
self.stack.append{object) def pop(self):
return self.stack.pop £ J def lengch{sel£):
return len(self.stack)
?初始
在类定义的第一行中,语句class Stack (object)将Stacie声明为一个object。使用圆括号是 Pythmi指定继承的方式——在这个例子中,Stack?承自object, object也是所有Python类型的根类 型.类定义中使用def语句定义了方法。毎个方法中的第一个参数始终指向对象本身。根据约定,该 参数使用名称班1£。涉及对象属性的所有操作都必须显式引用self变量,以双下划线开始和结東的方 法是特殊的方法。例如,用于在创建对象后初始化该对象?
要想使用类,可编写如下麻的代码,
?创建一个栈
B -
Stack(I
www.TopSage.com
1.15异常
17
9.push 1 "Dave") s.push(42) s.push([3,4,5]) s.pop() s.pop{)
#在栈中放入一#内容
# x的值为[3,4,5] t y的值为42 t删除s
这个例子创建了一个全新的对象来实现栈。伹是,栈与内置的列表对象几乎完全相同。因此,继 承list然后添加一个额外的方法也是可行的:
del s
class Stack(list):
?为#Ut口添加pushl)方法 #注意:列表已经提供了一个pop ()方法 def pushiself,object):
self.append(object)
通常,类中定义的所有方法只适用于该类的实例(即创建的对象)■,但是,也可以定义不同种类 的方法,如C++和Javafi序员所熟知的静态方法,例如:
class EventHanlder(object): @staticmethod def dispatcherThread ():
while (1):
*等待请求‘
?像函数一样调用方法
在这个例子中,@staticmetliod^方法声明为静态方法。@staticmethod是使用装钟器(decorator) 的一个例子,我们将在第6章中进一步介绍这个主题,
1.15异常
如果程序中出现错误,就会引发异常,并显示类似下面的迫踪消息=
EventHandler.dispatcherTread(1
Traceback (most recent call last):
File "foo.py", line 12, in <module>
IOError: [Ermo 2 ] Ko such file or directory: ‘ file.txt‘
该逍踪消息指出了所发生的错误类型及位置D通常情况下,错误会导致程序终止。但是可以使用 try和except语句捕捉并处理异常,如下所示:
try:
f = open("file.txt", except lOError as e:
print e
如果出现IOError,引发错误的详细信息将被放在对象e中,然后控制权被传递给except代码块 中的代码。如果出现其他类型的异常,对象e将被传递给用于处理这些异常的代码块(如果有的话)。 如果没有出现错误,except代码块中的代码将被忽略?处理完异常后,程序将继续执行紧跟在最后一 个except代码块后面的语句,程序不会返回到发生异常的位置。
raise语句用于手工引发异常。引发异常时,可以使用任意一个内置异常,如下所示:
raise RuntimeError ("Computer says non)
www.TopSage.com
18 第1章Python简介
你也可以创建自己的异常,这将在5.4.2节中详细介绍。
在使用异常处理时,如何正确地管理系统资源(如锁定、文件和网络连接)通常是一个棘手的问 题。为了简化此类编程,可以对某类对象使用with语句。下面的例子给出了使用互斥锁的代码:
import threading
mGssage_lock = threading. Lock ()
with message_lock:
messages.add(newraessage)
在这个例子中,with语句执行时会自动获取meSSage_lOCk?t象。当执行离开with代码块环境后, 锁定将被自动释放,无论with代码块内部发生了什么,这种管理都会进行。例如,如果出现一个异常, 当控制离开代码块坏境时将释放锁定,
with语句通常只适用于与系统资源或执行环境相关的对象,如文件、连接和锁定。但是,用户定 义的对象也可以定义它们自己的自定义处理。这一点将在3.9.11节中详细介绍。
1.16模块
随着程序变得越来越大,为了便于维护,需要把它分为多个文件。为此,Python允许把定义放入 一个文件中,然后在其他程序和脚本中将其作为模块导人。要创建模块,可将相关的语句和定义放人 与模块同名的文件中(注意,该文件的后缀必须是.pv>。例如:
# file : div.py def divide(a,b):
q = a/b
#如果a和b是整数,则q也是整数
r = a - q*b return (q,r)
要在其他程序中使用该模块:可以使用import语句:
import div
b = div.divide(2305. 29)
a.
import语句创建了一个新的命名空间,并在该命名空间中执行与.py文件相关的所有语句。要在 导入后访问命名空间的内容,只要使用该模块的名称作为前缀,正如上面例子中的div.divide() —
样。
如果要使用不同的名称导人模块,可以给import语句加上可选的蛇限定符,如下所示:
import div as foo
a,b = foo.divide(2305,29)
要将具体的定义导人到当前的命名空间中.可使用from语句:
from div import divide a,b = aivide(2305,291
t不再使用aiv前番
要把模块的所有内容加载到当前的命名空间中,还可以使用以下语句:
from div import *
与对象一样,dir()函数可以列出模块的内容,是进行交互式试验的有用工具:
import string
>>>
?> air (string)
X
y
r
I
www.TopSage.com
1,17获得帮助 19
[‘―builtins—?,
‘_it3mapL1 , ■_lower‘, ‘_swapcase’,
‘atoi‘, 1atoi_error1, ‘atol‘, ‘atol_error‘, ‘capwords‘,
doc
name
center‘, ‘count‘,
,■_idmap?,
—upper‘, "atof ‘, ‘ atof_error‘, capitalize1, digits 1, 1 expandtabs ‘ , 1 find1 ,
£ile__‘,
?>
1.17获得帮助
使用Python时,有几个快速获取可用信息的来源。首先,以交互模式运行Python时,可以使用 help。命令获得有关内置模块和Python其他方面的信息。单独输入help()将获得一般信息,而输入 Help (?模块名?)则可获得具体模块的信息,如果提供函数名称,help <)命令还可以返回该函数的详细
信息.
大多数Python函数都有描述该函数用途的文档说明。要打印这些文档的内容,只要打印_<1?:_ 属性即可。例如:
?>
print i‘BBubclass._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主题即可
中
以上是关于第一章: 未整理的主要内容,如果未能解决你的问题,请参考以下文章