●大标●
Python 之旅 (二)
文/马儿 (marr@linux.org.tw)
先声明一下,看完‘Python 之旅’并不会马上让每位朋友成为 Python 高手,这应该不是本专栏的用意,不过,引发读者对 Python 的兴趣,让大家一窥 Python 的应用实况,却是这一系列内容的企图。‘Python 之旅’主要定位为导读的角色,希望在本专栏告一段落之际,读者已具备应用 Python 的兴趣及预备进阶的实力,届时配合其他文件或书籍,相信能够达到快速吸收的效果。
行远必自迩,接下来的内容,将按部就班地介绍几种基本的资料型别、流程控制、语法结构、内建函式等,练功扎马步的工夫是省不了的,仅管可能乏味些,但透过实例操作,并和作业系统环境结合,希望能尽量与日常应用搭配,让‘Python 心法’更容易地融入读者的脑筋回路。
文中的范例,原则上都在 Mandrake Linux 7.2 环境里执行与测试,适用于其他版本的 GNU/Linux 应该并无问题,我将预设读者对于 Linux 已经具备基本之认识,特别是 shell 环境的操作,以及档案系统结构的认识,另外,C 语言的基本观念与技巧,也会有助于 Python 的学习,比如说,事先看过 A Book on C (作者是 Al Kelley 与 Ira Pohl) 的内容。这些简单的先备知识,可以成为多种程式语言学习背景支撑,建议读者不妨抽空予以纳入。
●中标●
从内建资料型别 numbers 谈起
Python 的数值资料型别基本分为四类:
|
数值资料型别 |
范例 |
|
1. 整数 (Plain Integers) |
7, -7, 256 |
|
2. 长整数 (Long Integers) |
7L, 10L, -777777777777L |
|
3. 浮点数 (Floating Point Numbers) |
7.0, 2e8, -7e10 |
|
4. 复数 (Complex Numbers) |
3+2j, -4-2j, 4.2+6.3j |
Python 里的整数型别,是以 C 语言的 long 型别来实作,也就是 32 bits 的精准度。直接依序看些操作范例:
example$ python
Python 1.5.2 (#1, Sep 30 2000, 18:08:36) [GCC 2.95.3 19991030 (prerelease)] on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> x = 5 + 14 / 7 - 3 * 2 ![]()
>>> x
1
>>> 5 / 2 ![]()
2
>>> 5.0 / 2 ![]()
2.5
>>> 2 ** 8 ![]()
256
可爱的四则运算。
别惊慌,5 / 2 答案是 2,这是正常的,如果你想得到 2.5 这样的答案,应用浮点数型别即可,只要搞清楚其运算的特性,Python 能够跑出相当令人满意的答案。
** 代表‘指数运算’,所以此处指的就是 28。
关于长整数所使用的 L 符号,事实上也可以用小写的 l 符号,不过还是建议使用大写的 L 符号,因为 1l 看起来实在太像 11 了。
>>> 10 ** 9
1000000000
>>> 10 ** 10 ![]()
Traceback (innermost last):
File "<stdin>", line 1, in ?
OverflowError: integer pow()
>>> 10L ** 10 ![]()
10000000000L
>>> x = 2147483647 ![]()
>>> x
2147483647
>>> x + 1 ![]()
Traceback (innermost last):
File "<stdin>", line 1, in ?
OverflowError: integer addition
OverflowError 就是指该型别‘数值满溢’的错误,1010 对整数型别而言已吃不消。
使用长整数后,其结果几乎已经到达‘予取予求’的地步,其有效范围只跟记忆体容量有关。
x86 PC 上,一般整数型别的最大极限为 231 - 1,即 214783647。
一旦超过 214783647 后,即显示整数型别的满溢错误。
例如 10L ** 100000 可就让我的 PIII CPU 忙上好一阵子了 (大约一分钟余)。有兴趣的朋友,可以试着以 time 来计时玩玩看。
example$ cat power_test1.py
print 10L ** 10000
example$ cat power_test2.py
print 10L ** 100000
example$ time --output=pt1 python power_test1.py
example$ time --output=pt2 python power_test2.py
Python 里的浮点数型别,是以 C 语言的 double 型别为基础来实作,我们可以透过 Python 的长整数来模拟许多浮点数计算的技巧,这在要求位数精准的场合上很实用,不过原则上,直接应用浮点数是来得更具效率。
>>> 7.3 ** 2.4
118.025232408
>>> 3.5e8 * 2.0e7 ![]()
7e+15
>>> x = 1.00000000001
>>> x
1.00000000001
>>> x = 1.000000000001 ![]()
>>> x
1.0
科学记号的运算。
超过精准位数的话,就会被截掉,使用时要小心。
下列是复数的运算,天啊,唤起我国中时代的回忆。
>>> x = (3 + 2j) * (4 + 9j) ![]()
>>> x
(-6+35j)
>>> x.real ![]()
-6.0
>>> x.imag ![]()
35.0
复数的指定方式,只需要在某个整数或浮点数之后,接个 j 或 J 符号即完成。
分别呼叫 x 的‘实部’与‘虚部’数值,这是应用了‘物件 x’的属性 (attribute) 功能。
拿 Python 的直译器当作计算机来用,也是非常方便。
>>> tax = 6.5 / 100
>>> price = 250
>>> price * tax
16.25
>>> price + _ ![]()
266.25
特别注意到上述的 _ (底线) 符号,它代表的是前一个计算值的结果。
还可以呼叫一些内建函式或载入函式模组。
>>> round(3.14159, 4) ![]()
3.1416
>>> abs(-3.000000001) ![]()
3.000000001
>>> import math ![]()
>>> math.sqrt(2) ![]()
1.41421356237
round() 是内建函式,可传回你想要的四舍五入精准位数值。
abs() 也是内建函式,可传回绝对值。
import math 就是载入 math 函式模组,随后便可呼叫其 sqrt() 开平方根功能。
以下的例子是载入 cmath 函式模组,以取得更多数值应用 (大抵用于处理 complex numbers) 的功能协助,其传回值属于复数型别。随 Python 程式所安装的系统档案里,就应该找得到一份 test_cmath.py 的测试范例。
example$ /usr/lib/python1.5/test/test_cmath.py
Calling sin(1.000000) = 0.841471
Calling log10(1.000000) = 0.000000
Calling acos(1.000000) = 0.000000
Calling asinh(1.000000) = 0.881374
Calling acosh(1.000000) = 0.000000
Calling exp(1.000000) = 2.718282
Calling tan(1.000000) = 1.557408
Calling cosh(1.000000) = 1.543081
Calling sqrt(1.000000) = 1.000000
Calling atanh(0.200000) = 0.202733
Calling sinh(1.000000) = 1.175201
Calling tanh(1.000000) = 0.761594
Calling atan(0.200000) = 0.197396
Calling log(1.000000) = 0.000000
Calling cos(1.000000) = 0.540302
Calling asin(1.000000) = 1.570796
PI = 3.14159265359
E = 2.71828182846
●BOX_BEGIN●
●标题●
数值资料型别的进阶资讯
有关完整的 number 型别资料,已经被整理于 Python Library Reference [1],有兴趣的朋友,欢迎前往挖宝。
另外,为了让 Python 在几何学与机械工程运算上能有更佳的表现,欧洲开发人员撰写了专为科学计算场合应用的 ScientificPython 2.2 模组 [2],其他进阶的科学运算技巧,诸如矩阵、行列式、向量运算、多项式运算、线性规划、3D 绘图模组等,都可以在此扩充模组中获得。对于教学人员或是研究人员,应该是相当值得参考的资源。
●BOX_END●
●中标●
流程控制 (Control Flow)
Python 的流程控制,主要由条件式 (conditionals)、回圈 (loops),以及例外 (exceptions) 所构成,此处的条件式和回圈,与其他程式语言学习的逻辑观念并无二致,纵使具有语法技巧上的小差异,其趣味性实在不高,我便不再赘述,读者手边若有第 16 期的Linuxer 杂志,可迳自翻阅参照‘Python 简介’一文的说明。接下来,我将直接以范例程式,带领读者认识 Python 流程控制的功能。
下列是一个简单的复利计算范例程式,程式执行后会输出计算结果。
example$ cat print_pr.py
principal = 1000
rate = 0.05
num_years = 5
year = 1
while year <= num_years : ![]()
principal = principal * (1 + rate)
print year, principal ![]()
year = year + 1
值得注意之处,在于 Python 的流程控制语法里使用了 : (冒号) 符号,以及大量的缩排效果 (indentation and block-structuring),这样的设计哲学可能跟 C、Perl 等语法相异其趣。
如果有人对于输出格式不够满意的话,可以采用 print "%3d %0.4f" % (year, principal) 叙述式,则可以得到新的输出结果:
1 1050.0000
2 1102.5000
3 1157.6250
4 1215.5063
5 1276.2816
也就是说,Python 的格式化字串,其处理方式和 C 语言里的 printf() 函式相仿,
举例来说,"%3d" 就可以将一个整数栏位,进行宽度为 3 的靠右对齐处理,而 "0.4f" 则可以将一个浮点数设定为四位小数的显示格式。
配合前述的基础,我们已经可以撰写个简单的 I/O 测试程式啰。
example$ cat high_low.py
number = 78
guess = 0
while guess != number :
guess = input(
“Guess a number: “)if guess > number :
elif guess < number :
example$ python high_low.py
Guess a number: 100
Too high. Try again.
Guess a number: 50
Too low. Try again.
Guess a number: 78
Just right.
下列的范例程式,可以产生每行显示 16 个字元的 ASCII 表:
example$ cat ascii.py
#!/usr/bin/python
i = 0
while i < 256 :
print chr(i), ![]()
if i != 0 and i % 16 == 0 : ![]()
i = i + 1
chr() 是 Python 的函式,读进一个 0 至 256 之间的整数后,会传回其对应之 ASCII 字元。注意到 chr(i) 后面所接的 , (逗号) 符号,它表示资料以紧接一个空白字元的方式输出。
i % 16 是为了控制每一行输出显示的字元数量。

图:ascii.py 执行后会显示 ASCII 表。
●中标●
序数资料 (sequence) 的操作
所谓序数资料型别,就是几个有序物件的集合,并可以自然数为其索引。Python 的序数型别包含有字串 (string)、串列 (list)、值组 (tuple)。分别介绍如下:
在 Python 当中,只要将几个文字包含在单引号、双引号、三引号里,就可以建立一个字串:
>>> a = 'hello world' ![]()
>>> b = "It's nice to learn Python" ![]()
>>> c = """
... this is a triple quote demo.
... see? we can go even cross lines :)
... remember to end it with another triple quote.
... """ ![]()
>>> a[0] ![]()
'h'
>>> b[5:18] ![]()
'nice to learn'
>>> c[-22:] ![]()
'another triple quote.\012'
分别示范的是单引号、双引号、三引号之字串建立方式。
字串的索引运算可以取得当中的元素 (element),字串 a 里的元素,是由 0 开始标序,所以 a[0] 是 h,而 a[1] 是 e。
字串的分割运算可以取得元素组。
注意到最后面的 \012,指的是换行符号。
字串某种角度来看,像是‘以字元为元素的串列’,因此,有关索引运算,以及分割运算,我会在即将介绍的串列部份中,更加详细地说明。
●BOX_BEGIN●
●标题●
原始字串 (raw string)
在某一个字串前面加上 r 或 R 字元,可以建立一个原始字串,例如 r
”hello world” 或 R”\the Python user” 等,原始字串经常被使用于 Python 的正规表示式 (regular expression) 场合上,尤其在处理脱逸字元时,是一项实用的特殊用法。●BOX_END●
串列 (list) 是以中括符号为设定格式,先让我们仔细观察其操作特性:
>>> x = [1, 2.0, 7, "eleven"] ![]()
>>> x[1] ![]()
2.0
>>> x[-2] ![]()
7
串列里可以包含多种资料型别,例如数值、字串等,甚至也可以再包含另一个串列在里头。例子中的 x 就是一个包含四个元素 (element) 的串列。
串列 x 里的元素,同样是由 0 开始标序。
以 -2 来表示从串列的后头往回标序,所以 x[-2] 是倒数第二个元素资料值。
我们可以使用分割运算 (slice),来截取串列项目里的某段子集,由于这项操作技巧太实用了,建议读者不但要搞懂,最好还能熟练。
>>> y = ["first", "second", "third", "fourth"]
>>> y[1:-1] ![]()
['second', 'third']
>>> y[:3] ![]()
['first', 'second', 'third']
>>> y[-2:] ![]()
['third', 'fourth']
>>> y[-2:-3] ![]()
[]
以串列 y 为例,其完整的索引 (index) 设定状况分析如下:
|
y = |
[ |
"first" |
, |
"second" |
, |
"third" |
, |
"fourth" |
] |
||
|
↑ |
↑ |
↑ |
↑ |
↑ |
|||||||
|
正向索引 |
0 |
1 |
2 |
3 |
4 |
||||||
|
负向索引 |
-4 |
-3 |
-2 |
-1 |
原则上,分割运算是采用 [m:n] 格式,m 为起点,n 为终点,不管是利用正向索引或是负向索引皆可。搭配图示对照后,可以很轻易地看出 y[1:-1] 指的是
分割运算若省略‘指明的’起点,则代表从最前面的元素取起。
分割运算若省略‘指明的’终点,则代表取至最后面的元素为止。
当起点落在终点之后时,分割运算的结果会是空串列。
最后,我们介绍的是值组 (tuple) 型别,乍看之下,它与串列的运作方式类似,不同处在于值组是以小括号符号为设定格式,串列是以中括号为设定格式,而且值组是不可变的物件,串列是可变的物件。
>>> t = ("this", "is", "a", 6, "element", "tuple") ![]()
>>> len(t) ![]()
6
>>> max(t) ![]()
'tuple'
>>> min(t) ![]()
6
值组建立的方式与串列相似,同样可以包含多种型别的元素。
使用内建运算元 len() 取得值组 t 的长度大小。
使用内建运算元 max() 与 min() 取得值组 t 的最大值与最小值。
另外,序数型别既然包括字串、串列、值组三种,在操作应用时,三者是具有共通之运算元的,比如之前已介绍过的索引运算、分割运算,以及 len()、min()、max() 等。
●中标●
物件的可变与不可变
什么是‘可变的’物件呢? 允许使用者对物件进行新增、修改、删除等动作,该物件则称为‘可变的’物件。事实上,每一种储存在 Python 程式里的资料,都是以物件的方式存在,而每一个物件的组成,则包括有识别字、资料型别,以及物件的值。当一个物件建立完毕之后,它的识别字和型别都不可以再更改,但它里面的值却有可能被修改,而可以被修改者,就是可变物件 (mutable),否则称为不可变物件 (immutable)。
>>> a = 256
>>> b = 'Python'
>>> print id(a), id(b) ![]()
134957540 134969832
>>> a is b ![]()
0
>>> print type(a), type(b) ![]()
<type 'int'> <type 'string'>
使用内建函式 id() 可以取得物件的识别字资讯,不过,大致只能得到一长串不甚具备意义的数字,藉以了解 a 与 b ‘确实’是两个不同的物件。
使用 is 运算元可以直接比较两个物件的识别字是否相同,由于我们知道 a 与 b 的识别字确实不同,因此运算结果是 0。
使用内建函式 type() 可以取得物件的型别资讯。
>>> y = [
“first”, “second”, “third”, “fourth”]>>> id(y)
135309760
>>> y[2] = '3rd' ![]()
>>> y.append('5th') ![]()
>>> y
['first', 'second', '3rd', 'fourth', '5th']
>>> id(y) ![]()
135309760
>>> y.sort() ![]()
>>> y
['3rd', '5th', 'first', 'fourth', 'second']
>>> id(y) ![]()
135309760
以索引运算来指定想要变更的资料值。
以物件方法 append() 来增加 '5th' 字串到串列 y 的最后面。
以物件方法 sort() 来将串列元素排序。
特别注意到,经过上述的操作及修改过程,串列 y 的识别字仍维持相同,表示串列 y 是可变物件。
●中标●
内建资料型别的概况整理
Python 提供将近二十余种不同的内建型别,我们目前几乎只见识到其‘冰山一角’,由基本的内建型别出发,将有助于认识所有的资料型别,对于尚未介绍到的资料型别,则希望有兴趣的读者能再持续深入研究。Python 是座宝山,最好是有兴趣同行,如此才容易逛出心得。
|
型别大类 |
型别名称 |
说明 |
|
None |
NoneType |
空物件 |
|
Numbers |
IntType LongType FloatType ComplexType |
整数 长整数 浮点数 复数 |
|
Sequences |
StringType ListType TupleType XRangeType |
字元字串 串列 值组 由 xrange(i,j,k) 函式传回值 |
|
Mapping |
DictType |
辞典集 |
|
Callable |
BuiltinFunctionType BuiltinMethodType ClassType FunctionType InstanceType MethodType UnboundMethodType |
内建函式 内建方法 类别物件 * 使用者定义函式 类别物件之实例变数 * 物件方法 物件方法 |
|
Modules |
ModuleType |
模组 |
|
Classes |
ClassType |
类别定义 * |
|
Class Instance |
InstanceType |
类别实例变数 * |
|
Files |
FileType |
档案 |
|
Internal |
CodeType FrameType TracebackType SliceType EllipsisType |
编译位元码 执行框架 例外追踪堆叠 由延伸分割所产生的型别 由延伸分割所使用的型别 |
表:内建之 Python 资料型别
* 值得注意的是,ClassType 和 InstanceType 同时分属于两类中。
善用 Python 内建之资料型别及物件模型,在专案开发上,是一个相当重要的技术议题,Python 长期以来的改版发展,一大课题便在改善各式物件及资料型别的效率。不过,实务上的做法,在要求效率的场合中,会建议直接以 C 或 C++ 来撰写程式,然后再汇出成为 Python 的延伸模组。
|
Integer |
12 bytes |
|
Long Ingeger |
12 bytes + (nbits/16 + 1) * 2 bytes |
|
Float |
16 bytes |
|
Complex |
24 bytes |
|
List |
16 bytes + 4 bytes * #_elements |
|
Tuple |
16 bytes + 4 bytes * #_elements |
|
String |
20 bytes + 1 byte * #_characters |
|
Dictionary |
24 bytes + 12 * 2n bytes, n = log2(nitems) + 1 |
|
Class Instance |
16 bytes + dictionary objects |
|
Xrange |
24 bytes |
表:内建资料型别于 32 位元电脑上的记忆体需求状况
●中标●
坐拥宝山贵在自知
在 Python 的原始程式码里,通常就会附随相当完整的文件,极富参考价值。只看附随的文件就足以自学成师,具备此番功力者,应该不是寻常人,不过,不假外求,却是学习历程中值得努力的一种境界,这往往代表着一个人独立思考求解的逻辑能力。为了让用功的读者早日认识及习惯 Linux 环境下的自学方式,在此简介 Python 文件的制作及阅读方式,相信对于其他的自学机会也能派上用场。
example$ cd /usr/share/doc/python-docs-1.5.2/Doc
此目录里存放了许多 .tex 档案,其实也就是 LaTeX 格式的档案,已经认识 LaTeX 的朋友,至此应该就能会心一笑,自个儿高兴地编译出 postscript 档案,而初次遇着 LaTeX 的朋友也无须纳闷,
由 .tex 档案制作出 .ps 档案的步骤,通常并不难。
example$ make
example$ ls *.ps
api.ps ext.ps lib.ps tut.ps
在一阵 TeX 编译过程之后,一切顺利的话,会看到上述四个 .ps 档案,即 Postscript 格式的档案,可以用于预视或直接送交雷射印表机处理。如果你遇到困难的话,八成是 LaTeX 相关套件安装不完备,先搁着也无所谓。
●BOX_BEGIN●
●标题●
入门 Python 工具书提示
1. Python 技术参考辞典
David Beazley、Guido Van Rossum 着
张裕益 译
书中第四章内容,即整理了各式资料型别的运算元及表示式,能够给读者完整的概念及资讯。而整本书也可以视作精简的自学手册,提供平常交叉参考之用。书的尺寸刻意浓缩,用意良善,不过,其排版方式不够明确,不容易让人快速寻得所需的资讯。
2. Teach Yourself Python in 24 Hours
Ivan Van Laningham 着
浅显白话的叙述方式,可以当作入门的教科书,里头的范例略显冗长,主要是诉求新手的直觉式学习,整本书啃起来像是软面包一般,易于下咽。书中的第三部份,专注于介绍 Python GUI 的内容,堪称一大特色,而第一部份则可做为本文的补充教材,适合有兴趣的读者进一步厘清基本概念。
3. The Quick Python Book
Daryl Harms、Kenneth McDonald 着
解说清晰是本书的特色,我非常喜欢书中的排版方式,加上内容广度相当大,让人觉得是一本物超所值的好书。透过诸如 JPython 与 Zope 的介绍,读者可以见识到 Python 学习后的应用实况。
4. Learning Python
Mark Lutz、David Ascher 着
早期 Python 的入门书籍还不若今日众多,Learning Python 很长一般时间成为 Python 使用者唯一的正式选择。这本书直接介绍 Python 核心而重要的部份,大抵都是设计概念层面的内容,依我个人的经验所示,花大约两个星期内的时间略读过它,效益上应会最佳。
●BOX_END●
cd 在 X Window 环境下,可以执行 gv (GhostView) 程式,用以预览 .ps 档案内容,如果设定好印表机,执行列印动作后,一份美观大方的文件便输出完成。
example$ gv tut.ps

如果有人对英文说明文件感到头痛,那么我建议仍试着‘略读’,特别是配合本 Python 专栏的系列介绍后,英文 Python 文件理应不再存有过大的鸿沟。
不过,可惜的是,我未能成功将 *.tex 档案转换成 html 档案格式,途中遭遇到一些状况,似乎是 latex2html 程式的问题。
只好请读者们自行前往 http://www.python.org/doc/ 网页,
线上阅读 HTML 版本的文件内容。值得注意的是,网站上所谓的 Current Documentation 通常指的是 Python 2.1 版文件,阅读时请配合自己手边使用之 Python 程式版本。
|
Tutorial |
深入浅出的教学手册。 |
|
Library Reference |
详细的函式解说。 |
|
Language Reference |
Python 语法解说。 |
|
Extending and Embedding |
说明如何将 Python 程式与其他程式语言进行扩充与结合。 |
|
Python/C API |
Python 的 C 程式底层介面说明。 |
|
Macintosh Reference |
专供麦金塔使用者的参考手册。 |
如果你懒得制作上述的文件,那么上网同样可以下载所有的档案,你可以在 http://www.python.org/doc/current/download.html 网页里,下载自己需要的部份:
|
HTML |
html-2.1.zip (2008 Kb) |
|
PDF (A4) |
pdf-a4-2.1.zip (3621 Kb) |
|
PostScript (A4) |
postscript-a4-2.1.zip (1958 Kb) |
|
LaTeX |
latex-2.1.zip (1000 Kb) |
值得一提的是,Python Tutorial 已有同好译成中文 [4],翻译品质极高,欢迎大家多多捧场,并给予译者一些鼓励。
●BOX_BEGIN●
●标题●
Python 2.1 问世
截稿之际,适逢 Python 2.1 正式版在网路上诞生,顺道在此公告周知。有兴趣的朋友可以由 http://www.python.org/ftp/python/2.1/ 下载,Linux 版本原始码压缩档约 4.1 MB,而 Windows 版本安装档案约 6.0 MB,完整安装约需 20 MB 硬碟空间。
从今年初开始进入 alpha 测试阶段的 Python 2.1,终于如期在四月间发表正式版,象征 Python Software Foundation 已成功辅助 Python 的发展。程式语言本身并没有重大的差异,但在安装亲和度与伴随之文件更新方面,已更上一层楼。Windows 环境下的使用者,下载新版程式后,可以尝试直接浏览新版的线上文件。
●BOX_END●
●中标●
Python 网路资源
[1] 最新版 Python Library Reference 线上文件
http://www.python.org/doc/current/lib/lib.html
[2] ScientificPython 网页
http://starship.python.net/crew/hinsen/scientific.html
[3] Python, Random Numbers, and Monte Carlo Simulation
http://www.linuxdev.net/features/articles/ldpymc_1.shtml
[4] 中文 Python 教学文件,译者署名为 周译乐
http://coder.9ug.com/language/script/python/tut_tw/tut.html
[5] Non-Programmers Tutorial For Python
http://www.honors.montana.edu/~jjc/easytut/easytut/easytut.html
[6] Python HOWTO Documents
http://py-howto.sourceforge.net/