●大标●

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 :

print Too high. Try again.

elif guess < number :

print Too low. Try again.

Print Just right.

 

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 :

print

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 字元,可以建立一个原始字串,例如 rhello 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] 指的是 secondthird 所组成的子串列,也就称为串列 y 的一个‘切片’(slice)。

 分割运算若省略‘指明的’起点,则代表从最前面的元素取起。

 分割运算若省略‘指明的’终点,则代表取至最后面的元素为止。

 当起点落在终点之后时,分割运算的结果会是空串列。

最后,我们介绍的是值组 (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/