python好书!!!

大标:Python 之旅 (一)

文/马儿

世界上有些事物,如同天生具备魔力一般,会让人不由自主地喜欢,Python 便是如此。对某些人而言,挑选一个常相为伴的程式语言,几乎就像‘终身大事’一般重要,以我为例,从 shell script 到 Perl,仅管这些工具已经足以解决日常事务,但总是少了‘感觉’。遇见 Python,才让我有‘就是它,找到了’的亲切感觉,没多久便投身为爱好者。仅管国外的 Python 迷与日俱增,台湾的朋友大多还是对它无甚认识,好东西要和好朋友分享,在此,邀请你和马儿一起踏上 Python 的探险之旅。

一、Python 两三事

起源

Python,大约是在一九九○年代的当头,由 Guido van Rossum 所发明。起初,Guido 原本只是 Amoeba 分散式处理作业系统计画中的一个成员,为了寻找一个较为合适的语言来从事这个计画,由此而触发他设计 Python 这一套程式语言的念头。早期设计上的构想,受到 Modula 程式语言系列的影响,当然也参考到 ABC、Icon 或 Smalltalk 这类物件导向式语言,在技术层面上确实融合多方优点,加上其发展的过程,有兴趣参与的朋友可以从 FTP 站自由取得原始码,或直接在新闻群组上参加讨论,使得 Python 的改版相当积极。直至目前为止,Guido 还是 Python 最关键的发展人员之一,并且组成名为 PythonLabs 的开发团队专注于版本改进。

音意

据一位在美国的网友表示,Python 读音类似 PIEson,在此提出供大家参考。至于以 Python 命名的由来,据 Guido van Rossum 表示,是取自于 BBC 着名的喜剧影集‘Monty Python's Flying Circus’,不过,由于 Python 的字意,最常见的是‘大蟒蛇’,因此 Python 的‘吉祥物’就是‘蟒蛇’的形象了。我认为这是 Python 最大的先天资产,开放源码社群里通常有个好玩的现象:‘专案的领袖最好具备高超的幽默感,以便产生独特的魅力。’这类的行事风格,在 Linux、Perl、Python 的通信论坛里时有所闻。

里程碑

Python 的发展,可以分成几个重要的阶段:

CNRI 时期:CNRI (Corporation for National Research Initiatives) 是资助 Python 发展初期的重要单位,Python 1.5 版之前的成果大部份都在此时期内完成。

BeOpen 时期:后来 Guido van Rossum 与 BeOpen 公司合作,此一期间将 Python 2.0 推出,甚至 Python 1.6 也同时问世,但原则上已经分别维护了。

DC 时期:目前 Guido 已离开 BeOpen 公司,将开发团队带到 Digital Creations (DC) 公司,该公司以发展 Zope 系统闻名,因此这项合作也颇受注目。

社群

Python 的发展,社群是相当关键的力量,为了因应学习及应用领域的日益扩大,由 Python 爱好者所组成的 PSA 提供许多实质的服务功能。PSA (Python Software Assocation) 致力于维护 Python 的发展与支援,成员包括公司团体及个人,而经常性的活动包括 python.org 网站维护、筹修研讨会,并收取会费以支应各项活动的开销。上述协会的日常工作,几乎都是由志工朋友所协助运作,有兴趣参加的朋友,可以造访 http://www.python.org/psa 的说明。

PSA 的会员具有一项福利,就是可以免费拥有‘星舰’(Starship Python) 网站上的帐号。星舰网站是由爱好者 Christian Tismer 所创立,舰上聚集各路同好,大家将平常的程式范例、文件资料整理分享,形容为‘Python 梁山泊’应是恰当。



图: 好汉云集的 Starship Python

除此之外,还有 Python Consortium 与 Python Foundation,分别专注于技术规格的发展,以及实质 Python 推广活动的落实及保障。国外对于开放源码专案的重视及支持,已经越来越明确,参与份子包括社群与厂商的共同投入,而且重点在于,他们是以有效而具体的运作方式,确保 Python 这类开放源码专案能不断向前演进,符合多数人的利益。

box

中标:第九届 Python 研讨会

使用 Python 的族群快速增加,这个现象,我们可以从第九届 Python 研讨会中看出个端倪。此次会议于三月八日及九日,在美国加州长岛的希尔顿饭店举行,这次会议当中,除了发表各式 Python 文件及学术报告...等活动的举行外,最主要的目的,就是提供所有来自世界各地的Python 程式设计人员,一个可以相互交换心得的场地。相较于前几届的研讨会,发表之文件及参与的人员,都比历年为多,足见 Python 愈来愈受到重视。如果您想要知道更多研讨会相关资讯,不妨参考 http://www.python9.org 网站内容。

box end

二、Python 的基本特质

省却编译连结的步骤

这点主要是与 C 语言这类传统程式语言进行比较,少了 compile 与 link 的步骤,专案开发流程与时效应可改善。不过,实际上 Python 把中介的 byte code 编译动作隐藏,让使用者没有查觉到相关的步骤动作。

省却变数类型宣告动作

这通常是描述式语言采用的原则,在此原则下,Python 程式容易显得简短,并具备弹性。

自动化的记忆体管理

所谓‘garbage collection’,使得程式设计人员不必花太多心思在记忆体管理的议题上。

内建许多高阶而实用的资料型态

善用其内建的资料型态,不但可以节省开发时间,执行效率上也相当令人满意。

轻易结合网路程式模组

热门的 TCP/IP (如 telnet、ftp、mail)、Web CGI scripting 工具、XML 解析器,网路上已有许多 Python 范例等待使用者利用。

物件导向式语言架构

具备物件导式语言的特性,诸如类别、多型、继承等设计,原本熟悉 C++、Java 的程式人员,应可沿用原有的物件概念到 Python 上。

跨平台与图形介面延伸

Python 族群投入极大努力于改进‘跨平台’议题,自然也就成为持续吸引潜在使用者的重要资产,Python 在 Windows 与 Linux 两大平台上的成熟度,差不多可以等量齐观,加上 wxWindows、wxPython 的发展,使用者不但到处都找得到 Python,而且也可以照自己的偏好,选择 Tk、Motif、GTK、Qt、MFC 等图形介面的搭配。

直译式之描述语言

类似直译式描述语言的例子,现在越来越常见而受重视了,如 Perl、REXX、Tcl、Scheme 等,由于 Python 是描述语言的一员,所以只要你之前有相关直译式语言的学习经验,相信概念上可以相通,如果你之前都没学过直译式语言,那么现在正是时候 :-)



图: Python 属于直译式语言,使用者可以在视窗当中进行交谈式互动。

Python 本身是用 ANSI C 所撰写成,目前可以在 UNIX、Linux、MS-DOS、MS Windows /95/98/NT、Macintosh、Amiga、Be-OS、OS2、VMS、QNX...各式不同的平台上编译及执行。这个特质使得大家几乎可以在不同尺寸、不同系统的电脑设备上,操作同样的语言环境,而没有局限的感觉,例如我在一台 486 笔记型电脑上以 DOS 环境执行 Python,让旧设备又多了新动力,倒也是一乐。

Box

中标:系统式语言与描述式语言

过去的十五年中,软体人员在撰写程式上,有了若干的改变,相当明显的一部份,就是程式设计人员,渐渐从采用 C/C++ 等系统式语言 (System Language),改变为采用 Perl 、Tcl、Pyhton 等描述式语言 (Scripting Language) 来撰写程式。在比较系统式语言与描述式语言之际,我们可以发现,传统的系统式程式语言的应用,是因为要用它替代原有的低阶语言 (如组合语言),让程式实际易于撰写维护,因此在型态及结构设计上,都较为严谨;而描述式程式语言的应用场合,主要的目的是为了要“嵌接”现有元件,因此在结构设计上,并没有如系统式语言般的严谨,在整个软体专案发展过程中,可以快速地完成开发目标。

可以这样说,系统式语言节省系统资源,或是发挥系统资源,而描述式语言节省的是‘程式人员的时间’。这年头,程式人员的时间恐怕是相当稀有,但话又说回,发挥系统资源正是资讯家电时代的研发重点,可见的未来,两者必有其专擅之处。

Box end

三、Python 与其他语言比较

原则上,Python 与其他语言之间,合作多于竞争,对于已存在的软体工具或模组,Python 都有机会予以整合,专案开发人员可视实际应用需求,在开发时间、物件重复利用、执行效率、除错效率,不同考量因素之间权变调整。仅管‘比较程式语言的优劣’本身不具太大的意义,但考虑到大家一定会提及这样的问题,所以还是讨论于下,仅供参考 :-)

  执行效率
学习效率
物件导向
图形介面
开发环境
大型专案应用
函式库工具

Python








Perl








Tcl/Tk








VB








C








C++








Java









表:简略的常见程式语言比较表 (优>佳>可>差)

Python V.S. C、C++

传统的 C/C++ 程式语言,在软体资讯世界里,绝对有其根本的重要性,这类系统式语言大抵都有如下的特性:严谨的型态宣告,完整的语法结构,繁复的编译过程。这些特性适于成为系统层面的开发工具,通常也就是中大型的软体专案,如果只是做些快速简易的小型程式,此时搬出 C/C++ 就显得‘杀鸡用牛刀’了。另外,程式的应用领域也值得考虑,如果想要撰写有效率的数值运算程式,里头使用大量的 for 回圈,那么 C/C++ 之类的工具程式仍是首要之选。

由于 Python 程式在执行时,通常处于‘半编译’状态(以 byte-code 型式存在),整体而言,执行效能必定是在 C/C++ 之后,除非是善用内建资料型态的 Python 高手,能够巧妙应用于股掌之间。实务的建议上,对于有意投身软体资讯领域的朋友而言,绝对不可能置身于 C/C++ 语言的影响之外,如何有效结合 Python 与 C/C++ 的应用优势,才是值得思考之处。对于一位已经熟悉传统程式语言工具的程式设计人员们而言,学习 Python 应无任何勉强之感,顶多只是取决于个人时间上的效益评估罢。

Python V.S. VB

Visual Basic 在企业界与 Windows 平台上,应用非常普遍。与 Python 相较,它们同属描述式语言,学习上都很简单,同样精于快速开发的小型程式应用领域,不过,大致有下列的差异处:

1. 两者的基本开发环境有显着不同,VB 是以视觉化的界面做为程式开发的环境,而 Python 传统是以文字编辑器的方式做为开发界面。若是以设计一个具备 Windows GUI 介面的程式为出发,VB 在开发上的确占了许多便宜,从另一个角度来看,若是整个程式包含介面部份,都是由程式设计人员自行撰写的话,则可以在专案维护上,掌握更多的细节。不过实务上,会采用 VB 的环境,八成短时间内也不会有所更动。

2. 如果有‘跨平台’的考量,那么 Python 会优于 VB 之上,如果只是在 Windows 环境执行,那么 VB 自然占有先天优势。据说新版 VB,将在语法结构上进行‘调整’,或许会对 VB 族造成影响。

3. 价格上,Python 一出生就‘自由/免费’了,欢迎任意拷备安装,至于 Visual Basic 的售价,可洽询 Microsoft 的报价说明,目前为止,还不是免费 :-)

Python V.S. Perl

Perl 是 Linux 平台上的语言大宗,由于发展历史悠久,使用族群广大,累积之工具模组十分惊人,加上两者性质与应用场合十分雷同,因此 Perl 与 Python 之间的‘比剑论战’总是迭起无休。以我个人的经验所及,Python 比 Perl 更适于发展大型严谨的应用程式专案,应是无庸置疑,通常问题是在于使用者本身的熟悉度,以及应用程式专案本身的规模上。如果专案规模不大,其实应该没有 Python 与 Perl 孰优孰劣的问题,甚至 Perl 的使用者很容易就可以从 CPAN (Comprehensive Perl Archive Network) 找到合适解决问题的工具,轮不到 Python 上场的机会。

除了外观与应用场合的相似外,Perl 与 Pyhton 在设计哲学及取向上,有着相当大的差异。Perl 语言的设计哲学,强调对原有语言使用者的调整适应,原有 C、shell、awk、sed 的老顾客,几乎可以快速在 Perl 里找到自己熟悉的部份,使得大量的程式开发族群,乐于跨个小步进入 Perl 的世界。Python 在设计上可说是独立的物件式语言,它的模组或语法惯例,肯定是无法为多数 C 语言老手所快速接纳,因此我会建议之前从无程式语言学习经验的朋友,考虑学习 Python 会显得更恰当,至于原本已是 C、Perl 惯用者,八成不必劳心劳力重新学习,而把生产力荒废了。

Pyhton V.S. Java

Python 与 Java 都具备了物件导向及跨平台的特性,不过,Python 在学习、写作、执行的效率上,都优于 Java。在知名度与支援状况,Java 现阶段都处于优势,而且 Java 环境已有相当完整的大型或小型的专业领域应用,显然这是 Python 短时之内难以赶上。若提到开发环境的专业支援,那么 Java 的相关产品可就琳琅满目,因此实务上,Java 会是相当好的专业级选择。



图:JPython 让程式人员同时享受 Java 与 Python 的优势

鱼与熊掌兼得

可以这样想像,传统的 Python 是以 C 语言来实作,因此也有人以 CPython 惯称之,现在则有一套以 Java 语言实作的 Python 程式语言,它被称为 JPython。JPython 维持所有 Python 的特性,差别就在于它是以 100% 纯 Java (一九九八年七月间通过认证) 所实作,以 JPython 和一般的 Java 程式进行互补合作,显得天衣无缝。它的应用场合,主要有下列三种:

1. 提供简化的嵌入式语言环境:Java 程式人员可以将 Java 函式库加进系统当中,以提供应用程式使用者一个直译语言介面,由于其底层以纯 Java 建构,大可放心地考虑将它与应用程式进行镶嵌,而不必担心在 Java 平台上的相容问题。

2. 提供交互式的直译语言环境:透过 JPython 的环境,可以让 Java 程式人员便于进行除错工作。

3. 提供应用程式快速开发环境:Python 程式通常比较简洁快速,撰写时间大多比功能相近之 Java 程式的时间快二分之一以上,这点隐含表示,Python 程式人员比 Java 程式人员更具开发生产力。就算有人执意要以 Java 语言写作程式,JPython 的程式人员同样可以巧妙地与其搭配,在开发程式时将两种任意混用。

有了 JPython 后,多数的程式语言新手们,应该可以稍微松口气,不必在‘既生瑜,何生亮’的矛盾心结里打转,直接学习 Python 即可 :-)

四、取得并安装 Python

具备基本概念后,让我们实际找到 Python 玩一玩。绝大多数的场合,在 Linux 环境下是不太需要伤脑筋的,安装好 Linux 之后,大部份的散布套件都会自动为您安装好 Python。以我的环境为例,安装的散布套件版本为 Mandrake 7.2,里头内建的是 Python 1.5.2:

python-1.5.2-12mdk
Python 主要执行档案与函式库

rpm-python-3.0.5-27mdk
处理 RPM 包装档案的模组

python-devel-1.5.2-12mdk
开发 Python 延伸程式时的标头档或模组

pythonlib-1.23-2mdk
内附之应用程式的工具模组

python-imaging-1.1-2mdk
提供影像处理能力的 Python Imaging Library (PIL)

postgresql-python-7.0.2-6mdk
处理 PostgreSQL 资料库连结的模组

python-docs-1.5.2-12mdk
Python 说明文件


表:Mandrake 7.2 预设的 Python 相关套件内容

在 Linux 的环境下,您只要直接键入 python,就可以进入 Pyhton 的交谈式编辑环境,或者以一般的文字编辑器撰写程式内容后,以 python my_prog.py 方式执行。如果要从交谈式编辑环境离开,请按 Ctrl + D 键 (代表送出 EOF) 即可结束,Windows 的环境则是按 Ctrl + Z 键结束。在 Python 的档案名称惯例中,.py 结尾者内含原始码,.pyc 结尾者为 byte-code 型式的编译档案。

如果,Python 1.5.2 版本无法满足你的需求,或是单纯急于尝新,想见识更新版本中所提供的程式库、函数资源,可以到 Python 总部(http://www.python.org/download) ,根据自己的作业环境需求,寻找并下载最新版的 Python 程式。

成功下载新版的 Python 档案后,你必须先用 tar 指令进行解压缩的动作,事先阅读 README 档案,并执行 ./configure 以产生 Makefile,最后执行 make 指令将档案完成编译,预设情况下,档案将会被安装在 /usr/local/lib/python<version> 的目录里头。想要顺利执行刚出炉的新版 Python 程式,别忘了仔细检查并设定好 $PATH 环境变数。如此一来,你应该就可以在 Linux 系统里同时拥有 1.5.2 与 1.6.x 或 2.x 的 Python 环境。

Box

中标:如果你是 Windows 使用者…

这里指的是 Windows 95/98/ME/NT/2000 环境,最单纯的安装方式,请下载 BeOpen-Python-2.0.exe 档案,也就是 Windows installer (安装程式),内含 Python 执行档、Tcl/Tk,以及 HTML 格式的文件档案。

如果你是进阶的 Windows 程式开发人员,那么可以考虑加装 Win32 extensions 这套 API 工具组,请下载 win32all-137.exe 档案,其中的 137 为其 build number,此为配合 Python 2.0 的版本。



图:在 Windows 环境下,会安装 GUI 及 command line 两种程式环境。

Box end

 

五、Python 的应用实例

Zope (Z Object Publishing Environment)

最具潜力者,当属 Zope 这套渐具名气的 Application Server 程式。在大型互动式网站的设计与实作上,Zope 能够提供相当完整的弹性,其物件及模组架构又能让网站管理人员、程式设计人员、内容维护人员,彼此独立作业,相互支援而又互不干扰。它的底层以 Python 物件组成,熟悉 Python 的朋友,甚至可以直接由原始码下手修改,而大多数的情况,是透过其管理介面或 DTML 语法方式,即可快速完成网站的扩充与管理。

Zope 有相当完整的资料库整合功能,透过各式 Product 模组可以与 Oracle、PostgreSQL、MySQL 等着名资料库紧密连结,另外,诸如授权、保安、备份、回复等机制,也在水准之上,堪称开放源码世界里的后起之秀。

有兴趣进一步认识的朋友,可以造访 http://www.zope.org 网站说明,Zope 的使用者社群与 Python 社群重叠性颇高,不过,两者仅是关系密切,操作 Zope 倒未必一定要具备完整的 Python 知识。


图: Zope 是一套开放源码版本的 Application Server 程式。

mailman (GNU Mailing List Manager)

传统通信论坛 (mailing list) 的伺服器软体,最着名者有 listserv 或 majordomo,近来 mailman 以其优良的管理及操作介面 (主要透过网页浏览器即可进行),强大的扩充功能,逐渐受到网路服务提供者的青睐,而 mailman 也是 Python 应用程式的一大杰作。此程式有效地展示了 Python 于字串处理与网路支援上的能力,有兴趣的朋友可以到 http://www.list.org 一探究竟。



图:mailman 是一套以 python 来开发的通信论坛管理程式

其他应用范例

着名的 Yahoo!mail,其 CGI 程式以 Python 撰写,系统规画结束后,实际程式码撰写仅在数月内完成。FermiLab 采用 Python 撰写系统工具,据程式人员表示,‘撰写过程让人感到通体舒畅’。卡内基美浓大学的 Alice 专案,开发 3D 影像处理系统,以 Python 为工具。另外,Red Hat Linux 的图形安装程式,以及 CLE 计画里的 pDict,都是采用 Python 语言撰写的范例。

六、展望 Python 的未来

‘没有一套程式语言是万能的’,学习 Python 的这段时间里,唯一真正说服我自己持续接触 Python 的原因,就只是‘有趣’而已。许多场合中,千万个技术考量,往往比不过一个‘不理性因素’,况且这通常会是最真诚而持久的动力因素。Python 在 2.0 版本问世之后,继续往 2.1 前进,还有令人感兴趣的是,Pippy (Python for the Palm) 的出现,意味着日后的 Python 迷也可以在 PDA 等嵌入式设备里遇见 Python。另外,Microsoft 曾经主导的 COM/DCOM,以及目前在大力推广的 .net 环境,Python 都已经加入整合行列,在在显示,未来的发展之路还很宽广。



图:Pippy 是 Palm 环境上的 Python 程式工具。

Python 的优势,在于它有效满足日常工作的需求,语法上优雅而让人易于学习,适合成为入门程式语言的踏脚石,况且,安装与使用也都不必花钱嘛,找本参考书籍,或是连上网路说明文件,都是一般人能力可及的。有关平台与介面的环境,我会建议以 Python 配合 wxWindows,也就是 wxPython 这套工具,相关的介绍会在日后的专栏中陆续提及,敬请期待。

对新手而言,与其一直伤脑筋‘该学哪种程式语言’,倒不如直接投入学习,Python 确实是一套‘进可攻、退可守’的良好入门工具。台湾的 Python 用户似乎还是很少,这点会造成学习上的困扰,因此,本文除了希望有助于推广之外,也期盼同好们藉此机会齐聚一堂,大家交流心得与经验。

七、Python网路资源

要取得 Pyhton 的相关资源,除了到各大书店里去寻找现成的书籍外,在网路上寻求更新的文件资讯,绝对是聪明的选择,列举几个重要的网址,值得参考看看:

Python 的官方网站

http://www.python.org

Pyhton 资源的大本营,不论是与 Python 相关的文件资源及程式,大部份都可以由此取得。

Object-Oriented Language: Python

http://www.cetus-links.org/oo_python.html

不看可惜的资源整理。

Python Journal

http://www.pythonjournal.com

Python 核心发展的线上文件,极具研究价值。

What's New in Python 2.1

http://www.amk.ca/python/2.1

想知道最新发展版本 Pyhton 2.1 的改进之处吗?

O’Reilly 的 Python 书籍资讯

http://www.oreilly.com/catalog/lpython

Learning Python 只是一例,市面上已经出现越来越多优质书籍。

Python 教学文件

http://coder.9ug.com/language/script/python/tut_tw/tut.html

入门 Python 极佳的导引资料。

Dive Into Python

http://diveintopython.org

这个网站上,提供文件给‘具备经验的 Python 程式设计人员’。

除了参考网站上的资源外,利用 BBS 的讨论区,也可以取得不少资讯。请 telnet 到 linux.twbbs.org 或 bbs.cynix.com.tw 站上,寻找 Python 板面,上面有不少的题材,可以供您参考并和他人讨论。      

作者: shiqiiang   发布时间: 2004-06-30

●大标●

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 字元,可以建立一个原始字串,例如 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] 指的是 “second” 与 “third” 所组成的子串列,也就称为串列 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      

作者: shiqiiang   发布时间: 2004-06-30

Python 之旅 (三)

文/马儿 (marr@linux.org.tw)

上期内容当中,读者已经浏览过 Python 语言里的一些基本资料型别元件,我们将接续地看到更多实用而有趣的范例。本期的重点摆在字串、函式、模组、系统资讯的处理上,这些学习经验仍然是 Python 程式设计的重要基础。

连续数值的指定: 内建函式 range()

range 是一个便利的内建函式,可以传回一组整数值的串列。

>>> range(7) Œ

[0, 1, 2, 3, 4, 5, 6]

>>> (mon, tue, wed, thu, fri, sat, sun) = range(7)

>>> mon

0

>>> sun

6

>>> range(-5, 5) Ž

[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]

>>> range(0, 10, 3)

[0, 3, 6, 9]

Œ range 的预设值由 0 为起始。

将一个值组与 range 函式所产生的串列进行对映,所以指定结果是 mon 为 0,而 sun 为 6。

Ž 可以为 range 函式指定起始值与终止值,如 (-5, 5) 是指 -5 到 4 所形成的整数值串列。

先前的例子都省略了间隔值的设定,而使用其预设间隔值 1,在此我们可以加入间隔值的设定,如例中的最后一个参数 3。

下列的简单范例,可以将一组资料进行编号。

>>> weekday = ('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')

>>> for day in range(len(weekday)):

... print day, weekday[day]

...

0 mon

1 tue

2 wed

3 thu

4 fri

5 sat

6 sun

实务程式写作上,range函式 与 for 回圈经常搭配。还记得上期内容里的 ascii.py 范例吗? 我们可以使用 for 回圈与 range 函式加以改写,来个温故知新。

example$ cat ascii.py

#!/usr/bin/python

for i in range(256) :

print chr(i),

if i != 0 and i % 16 == 0 :

print

相较之下,程式码本身显得简化了,但语法型态与 while 不同,读者应用时大抵挑自己习惯者即可。

资源与效率的抵换

除了 range() 之外,还有个 xrange() 内建函式,不但名称相近,功能可说完全一致,其传回值最后是相同的。差别在于 range() 会以串列资料型别来储存所产生的传回值,实际在记忆体里占有完整空间,而 xrange() 则是传回一个 XRangeType 的物件,并不会在记忆体里占有太多储存空间,而是等到实际存取资料时才继续算出其值。所以在大笔资料的实务应用上,使用 range() 会占用较多记忆体,但效率快些,而 xrange() 则节省记忆体空间,而牺牲了效率。请读者善用 Python 的设计特性,依场合需要自行选用合适的函式。

>>> range(1000000)

>>> xrange(1000000)

如果你的电脑系统资源丰富,上述两个例子‘看不出差别’,可以继续尝试更大的输入值来测试。

函式 (function) 的使用

函式功能在程式语言里是必备的,不管是函式或模组的写作,都要求程式员尽可能做到易读易懂、功能独立、可重复使用的程式片段,‘避免重造轮子’,这样的设计理念,在 Python 语言里便极受重视。相信读者至少已经熟悉一种其他程式语言的函式功能,我们可以直接观察下列的范例:

>>> def fact(n):

... """Return the factorial of the given number."""

... r = 1

... while n > 0:

... r = r * n

... n = n - 1

... return r

...

>>> fact.__doc__

'Return the factorial of the given number.'

>>> fact(5)

120

>>>

函式还是一个物件,其指定方式,是以保留字 def (即 define 之意) 为首,余下的函式内容,同样要按照‘Python 的程式码缩排原则’,否则会产生‘IndentationError’的错误讯息。惯例上,函式的第二行会是一段‘三引号注释字串’,即 """ 所括夹的文字,称为‘文件字串’(documentation strings),我们可以透过如 fact.__doc__ 的物件方法,再把文件字串内容显示出来,这样的实务惯例,通常在大型而正式的 Python 程式开发专案里显得有用。

fact() 是一个阶乘函式范例,请注意到最后一行的保留字 return,如果少了 return 叙述 [1],则预设会以 None 值传回。以范例函式 fact() 来看,n 是函式的参数 (argument),其传回值 r 是 n 的阶乘结果。

保留字、识别字、内建函式名称

程式语言里的保留字 (reserved words) 与识别字 (identifiers),是用来识别变数、函式、类别、模组、物件的名称,我们先前见过如 print、is、while、for、def、return 等,都是 Python 保留字的范例。另外,以 _、__ 为开头的识别字名称,许多是 Python 的保留识别字,如 __doc__、__name__、__builtins__ 等,它们通常有特殊意义,日后我们会看到越来越多这样的例子。至于内建函式名称,读者已经见过的,如 chr()、id()、len()、max()、range() 等。值得注意的是,上述识别字相关名称,都是大小写有别的 (case sensitive)。对于打算长期与 Python 厮混的朋友而言,应该有必要手边准备一份保留字相关资讯。

Python 识别字指定规则:

http://www.python.org/doc/2.0/ref/identifiers.html

Python 所有保留字资讯:

http://www.python.org/doc/2.0/ref/keywords.html

>>> def fact(n=10):

... """Return the factorial of the given number, with the defult input value."""

... r = 1

... while n > 0:

... r = r * n

... n = n - 1

... return r

...

>>> fact(5)

120

>>> fact()

3628800

>>>

上述程式片段示范了函式预设输入值的设定方式,试试以 fact(5) 呼叫范例函式,会得到传回值 120,而以 fact() 呼叫,则会传回以 10 为预设输入值的 3628800。

>>> def power(x, y=2):

... r = 1

... while y > 0:

... r = r * x

... y = y - 1

... return r

...

>>> power(2, 4)

16

>>> power(3)

9

>>> power()

Traceback (innermost list):

File "<stdin>", line 1, in ?

TypeError: not enough arguments: expected 1, got 0

>>> power(2, 4, 3)

Traceback (innermost list):

File "<stdin>", line 1, in ?

TypeError: too many arguments: expected 2, got 3

>>>

一个函式可以设定多个输入值,上例 power() 函式可以接受两个输入值,但至少需要 (expect) 一个输入值,因为参数 y 有个预设值 2,输入参数时可以省略之。以 power(2, 4) 呼叫时,会成功传回 16,以 power(3) 呼叫时,会自动以 power(3, 2) 为输入值,传回 9。如果以 power() 呼叫,则会产生 TypeError 的错误讯息,它会明确告知‘参数不足’,你必须至少输入几个参数,如果是以 power(2, 4, 3) 来呼叫,则会得到‘参数过多’的 TypeError 讯息。

>>> power(y=4, x=2)

16

>>>

另一个有用的应用技巧,称为‘关键字派定法’(keyword passing),是以类似 power(x=2, y=4) 方式来呼叫,明确地将参数值配合变数名称通知函式,甚至也可以用 power(y=4, x=2) 方式来呼叫,如此一来,两者的呼叫结果是完全相同,意即其参数值顺序可以任意改变。

我们接着来看一些任意个输入值的例子,相信其实用价值极高。

>>> def maximum(*numbers):

... if len(numbers) == 0:

... return(None)

... else:

... max = numbers[0]

... for n in numbers[1:]:

... if n > max:

... max = n

... return max

...

>>> maximum(3, 8, 5)

8

>>> maximum(2, -5, 9, 1, 7)

9

>>>

函式 maximum() 的用意很明显,我们可以输入任意个数的数值,而它最后会传回最大值。例如 maximum(3, 8, 5) 会传回 8,而 maximum(2, -5, 9, 1, 7) 会传回 9。值得注意的地方,就是其处理不定参数个数的技巧,参数指定以 *numbers 方式代表,而 numbers 本身是一个值组 (即 tuple,而非 list)。函式的演算规则倒简单,先把第一个数 numbers[0] 设为最大值,再将剩下的数 numbers[1:] 所形成之值组,丢进 for 回圈,以‘暴力法’比大小。

下列的例子,将参数设定的可能状况大抵做了整合介绍,我们可以一窥函式参数派定的相关细节,值得读者反覆测试观察。

>>> def args_func(a, b, c=9, *other1, **other2): Œ

... return [a, b, c, other1, other2.items()]

...

>>> args_func(1, 2, 3) Ž

[1, 2, 3, (), []]

>>> args_func(b=1, a=2)

[2, 1, 9, (), []]

>>> args_func(1, 2, 3, 4, 5, 6, 7, 8, 9)

[1, 2, 3, (4, 5, 6, 7, 8, 9), []]

>>> args_func(1, c=3, b=2, d=4, e=5) ‘

[1, 2, 3, (), [('d', 4), ('e', 5)]]

>>> args_func(d=4, e=5)

Traceback (innermost last):

File "<stdin>", line 1, in ?

TypeError: not enough arguments; expected 2, got 0

Œ 范例函式 args_func() 可以输入三个 (以上的) 参数,参数名称分别是 a、b、c,其中 c 有预设值为 9,a 与 b 是必要的输入参数。而 *other1 用以指定 a、b、c 关键字之外的参数值 (不定个数),**other2 则是用以指定 a、b、c 关键字之外的派定值 (同样是不定个数)。

函式 args_func() 非常简洁,直接把所有的输入参数值以串列资料型别传回。其中的 other1 属于值组资料型别,而 other2 则是辞典集 (dictionary) 资料型别。别慌,稍后会为读者解说辞典集的相关细节。

Ž 给定三个参数值,它们会分别成为函式 args_func() 的 a、b、c 的设定值,此时 other1 与 other2 都是空空如也。

使用关键字派定法来指定 a 与 b 的参数值,而使用 c 的预设参数值。

给定了九个参数值,前三个依序成为 a、b、c 的参数值,后六个数值则成为值组 other1 的元素内容。

‘ 给定了五个参数值,第一个成为 a 的参数值,b 与 c 以关键字派定法来指定,而最后的 d 与 e 则成为不定个数的关键字派定值,它们被辞典集 other2 所收留了。

内建资料型别 dictionary 的使用

辞典集是 Python 里的内建映射物件 (mapping object),也就是由一个物件集合来作为另一个物件集合的键值索引。映射物件和之前谈过的序数资料相较,在概念上有扩充、补强的涵意,善用映射物件的特性,可以协助我们将看似复杂的问题,以相当直觉的方式解决。两者具有若干不同之处,例如序数以索引运算作为其取值方式,映射物件的元素成份来源很有弹性,映射物件是不做排序的,而且映射物件是可变物件。

●TABLE_BEGIN●

表: 序数型别与映射型别的比较

序数型别
映射型别

以索引运算作为其取值方式。
以 keys()、values()、items() 等内建函式来取值。

以整数作为其索引(index)。
以不可变物件作为其键(key)。

元素具排序效果。
元素储存时不做特定排序。

不一定都属于可变物件。
辞典集是可变物件。


●TABLE_END●

在功能上,辞典集的实作相当于资料结构里的杂凑表 (hash table) 或是相关阵列 (associative array),所以你会看到‘键-值’(key-value pairs)的表示法。通常我们会以字串来做为辞典集的 key,因为有意义的字串可以带来‘望文生义’的效果,不过,一定要以不可变物件来做辞典集的 key,而 value 的部份就全无限制了。

>>> x = [] Œ

>>> y = {} Œ

>>> x[0] = 'Beatles' Œ

Traceback (innermost last):

File "<stdin>", line 1, in ?

IndexError: list assignment index out of range

>>> y[0] = 'John Lennon'

>>> y[1] = 'Paul McCartney'

>>> y[2] = 'George Harrison'

>>> y[3] = 'Ringo Starr'

>>> y[0] + " and Yoko Ono" Ž

'John Lennon and Yoko Ono'

>>> y

{3: 'Ringo Starr', 2: 'George Harrison', 1: 'Paul McCartney', 0: 'John Lennon'}

>>> y.keys()

[3, 2, 1, 0]

>>> y.values()

['Ringo Starr', 'George Harrison', 'Paul McCartney', 'John Lennon']

>>> y.items()

[(3, 'Ringo Starr'), (2, 'George Harrison'), (1, 'Paul McCartney'), (0, 'John Lennon')]

>>>

Œ 分别起始建立一个空串列 x 与一个空辞典集 y。由于无法指定不存在的索引值给串列,所以 x[0] 的指定动作宣告失败,Python 回报 IndexError 错误讯息。

对于辞典集而言,并无上述的困扰,修改或增添元素资料均无限制。此例中,我们分别指定了 y[0]、y[1]、y[2]、y[3] 四笔元素资料。

Ž 可以针对元素进行各式运算,例如我们拿字串 y[0] 与另一字串合并显示。

注意到辞典集的储存,并没有特定的顺序方式,如果想要依特定的排序方法处理资料,可以另寻变通方法。

示范辞典集最常见的内建函式,即 keys()、values()、items(),它们都是传回串列物件。

>>> Beatles = {'leader':'John','bass':'Paul','guitar':'George','drum':'Pete'} Œ

>>> Hurricanes = {'drum':'Ringo'}

>>> Beatles.update(Hurricanes) Ž

>>> Beatles

{'drum': 'Ringo', 'leader': 'John', 'bass': 'Paul', 'guitar': 'George'}

>>> Beatles.get('leader', "Not available")

'John'

>>> Beatles.get('manager', "Not available")

'Not available'

>>>

Œ 我们为一个叫做 Beatles 的乐团建立辞典集,指定其键值及元素值,共计四项元素。

另一个叫做 Hurricanes 的乐团,其辞典集元素,只有鼓手的设定资料。

Ž 透过 update() 物件方法,我们更新了 Beatles 鼓手的设定资料。

get() 物件方法是询问 Beatles 里是否有 leader 此一键值,若存在则传回其对应之元素值,否则会传回后头的字串资料。

相信读者至此已对辞典集有了基础认识,其他辞典集相关的物件方法及运算元,请参考表格内容说明。值得一提的是,Python 里的辞典集实作得相当有效率,就算和串列型别相较,你也应该会满意于它的便利与速度,适当的话,可以考虑多加使用。

●TABLE_BEGIN●

表: 辞典集的方法和操作

项目
说明

len(dict)
传回辞典集 dict 里的元素个数。

dict[k]
传回键值 k 的元素内容。

dict[k]=v
将 dict[k] 的内容设定为 v。

del dict[k]
将 dict[k] 元素项目移除。

dict.clear()
将辞典集 dict 所有元素项目全部移除。

dict.copy()
将辞典集 dict 整个复制。

dict.has_key[k]
如果辞典集 dict 含有键值 k 的话,则传回 1,否则传回 0。

dict.items()
以值组 (key, value) 的串列型别传回辞典集中所有元素。

dict.keys()
传回辞典集 dict 的所有键值。

dict.values()
传回辞典集 dict 的所有元素值。

dict.update(other)
将辞典集 other 所有物件更新至辞典集 dict 当中。

dict.get(k [, other])
如果 dict[k] 存在则传回 dict[k],否则传回 other。


●TABLE_END●

模组 (module) 的使用

简单地说,模组代表着某个档案名称,该档案名称必须以 .py 延伸档名作结,我们可以到 Python 的安装目录一窥究竟,以 Linux Mandrake 7.2 为例,其安装目录为 /usr/lib/python1.5。目录里包含类似 string.py、os.py、find.py 的档案,我们可以透过 import string, os, find 之类的程式语法呼叫这些模组内容。也可以直接阅读这些 .py 档案的程式码,相信部份档案的内容,对你而言已不再全是天书。

别误会了,模组本身也可以是编译过的档案,如 .pyc 或 .pyo 档案即属此类。在系统目录里存在这类档案,通常可以达到执行加速的效果。

实际动手撰写自己的模组之前,我们得先认识内建函式 dir() 的功能,它可以将许多物件内部的资讯显示出来。

>>> dir() Œ

['__builtins__', '__doc__', '__name__']

>>> dir(__doc__)

[]      

作者: shiqiiang   发布时间: 2004-06-30

>>> print __doc__

None

>>> print __name__ Ž

__main__

>>> type(__builtins__)

<type 'module'>

>>> dir(__builtins__)

['ArithmeticError', 'AssertionError', 'AttributeError', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'FloatingPointError', 'IOError', 'ImportError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplementedError', 'OSError', 'OverflowError', 'RuntimeError', 'StandardError', 'SyntaxError', 'SystemError', 'SystemExit', 'TypeError', 'ValueError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', 'abs', 'apply', 'buffer', 'callable', 'chr', 'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dir', 'divmod', 'eval', 'execfile', 'exit', 'filter', 'float', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'len', 'list', 'locals', 'long', 'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round', 'setattr', 'slice', 'str', 'tuple', 'type', 'vars', 'xrange']

>>> print __builtins__.__doc__

Built-in functions, exceptions, and other objects.

Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices.

>>>

Œ 当我们身处新启动之 Python 交谈环境里,输入 dir() 可以显示 local symbol table的名称串列,共计三个。

进一步提供 __doc__ 给 dir() 做为参数,传回空串列,表示 __doc__ 物件已无相关属性 (attributes)。print __doc__ 显示其为 None 物件。

Ž __name__ 是一个字串物件,表示目前执行‘程式’的名称,其值为 __main__。

__builtins__ 则是一个模组物件,持续以 dir(__builtins__) 观察,可以显示模组 __builtins__ 的全部属性。

显示 __builtins__ 的 __doc__ 属性内容。

注意到,每个正在执行的主要程式,其程式名称 (即 __name__ 属性) 会是 __main__,如果是以模组型态被 import 进来,那么该模组程式便会以原本档案名称为 __name__ 的值。请观察下列程式范例的说明,两个极其简化的‘土制模组’。

example$ cat other_mod.py

#!/usr/bin/python

print "this is from other_mod."

print __name__

example$ chmod 755 other_mod.py; ./other_mod.py

this is from other_mod.

__main__

example$ cat my_mod.py

#!/usr/bin/python

"""Simple module example for illustrating how __*__ works."""

import other_mod

print "this is from my_mod."

print __name__

example$ chmod 755 my_mod.py; ./my_mod.py

this is from other_mod.

other_mod

this is from my_mod.

__main__

 

被 import 的模组档案,其内容会被执行,所以范例 my_mod.py 在执行之后,会先读进 other_mod.py 的程式片段,接着才是 my_mod.py 的程式片段。请特别留意 __name__ 属性值的变化,这项控制技巧经常被使用。

模组的搜寻路径

Python 实际搜寻模组路径的设定值,可以由 sys 模组里的 path 变数值取得。

>>> import sys

>>> sys.path

['', '/usr/lib/python1.5/', '/usr/lib/python1.5/plat-linux-i386', '/usr/lib/python1.5/lib-tk', '/usr/lib/python1.5/lib-dynload', '/usr/lib/python1.5/site-packages', '/usr/lib/python1.5/site-packages/PIL']

>>> import marr

Traceback (innermost last):

File "<stdin>", line 1, in ?

ImportError: No module named marr

>>>

上述的搜寻路径是有顺序性的,也就是说,import 所呼叫的模组名称,依序第一个找到的路径便会马上回传使用。如果找不到 import 的模组名称,则会传回 ImportError 的错误讯息。以载入 foo 模组为例,其实际寻找模组的过程顺序如下:

1. 是否存在名为 foo 的目录,并且里头含有该模组的档案。

2. 是否存在 foo.so、foomodule.so、foomodule.sl 或是 foomodule.dll

3. 是否存在 foo.pyo

4. 是否存在 foo.pyc

5. 是否存在 foo.py

以一个 .py 的 Python 原始码档案而言,经过编译后,会产生一个名为 .pyc 的 bytecode执行档,当寻找某个模组名称时,要是 .py 档案的日期不比 .pyc 档案来得新,Python 直译器会直接将编译好的 .pyc 档案载入,若是 .py 档案的日期比 .pyc 档案来得新,通常就表示 .py 档案内容已更新,Python 直译器会重新编译之,以产生新的 .pyc 档案,然后才进入载入动作。而 .pyo 档案只有在直译器以 -O 选项启动之后才会产生,这类档案里的资讯通常比 .pyc 档案来得多,包含有原始程式的行号以及除错资讯,因此 .pyo 档案的载入速度会较慢,但程式的执行速度会较快。

而 .pyc 或是 .pyo 档案的编译动作,是在程式里头呼叫 import 后才会发生,对 Python语言来说,模组档案不止是设计概念的切割,它更从强化模组执行效率的角度,鼓励程式员善用模组档案功能。如果自制的模组档案越来越多,其应用自然越显重要,此时便要认真为自制模组找个适当的存放路径,比较常见的方式之一,是设定相关的环境变数值,例如变数 PYTHONPATH。

●TABLE_BEGIN●

表: Python 相关环境变数设定

变数名称
说明

PYTHONDEBUG
与 python -d 启动模式相同。可产生 Python 的语法解析除错资讯。

PYTHONHOME
与模组搜寻路径设定相关的变数。

PYTHONINSPECT
与 python -i 启动模式相同。以交谈模式来执行 Python 程式。

PYTHONOPTIMIZE
与 python -O 启动模式相同。以最佳化模执行 Python 程式。

PYTHONPATH
增加模组搜寻路径。

PYTHONSTARTUP
交谈模式就绪前所执行的程式路径。

PYTHONUNBUFFERED
与 python -u 启动模式相同。记录未做缓冲的二元标准输出输入。

PYTHONVERBOSE
与 python -v 启动模式相同。执行过程详列相关处理资讯。


●TABLE_END●

名称空间 (namespace) 及其有效领域 (scope)

回过头来,我们再次透过 dir() 来观察模组档案载入后的物件变化情况。请将现行目录设定在 my_mod.py 档案存放的目录,然后进入 Python 的交谈环境。

●图●

图: 名称空间阶层示意



●图●

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

>>> dir()

['__builtins__', '__doc__', '__name__']

>>> import my_mod

this is from other_mod.

other_mod

this is from my_mod file.

my_mod

>>> dir()

['__builtins__', '__doc__', '__name__', 'my_mod']

>>> dir(my_mod)

['__builtins__', '__doc__', '__file__', '__name__']

>>> print my_mod.__doc__

Simple module example for illustrating how __*__ works.

>>> print my_mod.__file__

my_mod.py

>>>

整个运作过程,依序可以分解如下:

1. 每次 Python 启动时,它会产生一个名为 __main__ 的模组物件,此物件本身当然有其相关资讯,所以我们可以透过 dir() 来取得。

2. 每次呼叫 import 内建函式之后,Python 会载入另一个模组物件,并将其物件相关资讯并进 __main__。

3. 由于两个模组物件的变数名称,有若干重复之处,如上述例子里的 __builtins__、__doc__、__name__,在取用变数资讯时,必须有办法加以区别才行。

4. Python 区别变数资讯的方法,如上例所示,是类似 __doc__ 与 my_mod.__doc__ 这样的不同表示法。

5. 每个模组物件,都会伴随一份物件资讯,以辞典集型别储存在系统当中,而这样的物件资讯内容,包括其变数、函式、物件等,都成为辞典集里所记录的键值或属性值。

6. 上述的辞典集物件资讯,我们把它简称为‘名称空间’(namespace)。

7. 内建函式 dir() 的功能,可以把名称空间的名称资讯全部列出。

除了基本的资料型别之外,Python 里的每一个模组物件都拥有自己的名称空间。下列是一个范例程式,可以协助列出模组物件的名称资讯。

example$ cat namespace.py

#!/usr/bin/python

import sys

k = sys.modules.keys()

print "Keys:", k

print "-" * 30

for i in k:

if i == "__main__":

print ">>>", i, "__dict__", sys.modules.__dict__

print "-" * 30

print dir()

在实务设计上,Python 程式员会运用模组的方式,将物件做适当的切割,切割好的程式可以被包在一个 Python 类别库 (package) 里,以便进一步有效管理大型的软体专案。有了函式、模组、类别库等一系列的架构,我们便可更直觉地管理软体专案的物件体系。着名的 Zope 系统便是这样的专案管理实例 [2],其整套系统广泛应用类别库的技巧,至今仍在积极发展中。

其他有用的函式

为了方便接续的内容,我们先来认识一个实用的模组,称为 string,顾名思义,可用于协助处理字串物件。

>>> import string

>>> date = "Fri May 18 CST 2001"

>>> piece1 = string.split(date)

>>> piece1

['Fri', 'May', '18', 'CST', '2001']

>>> time = "12:03:27"

>>> piece2 = string.split(time, ':')

>>> piece2

['12', '03', '27']

>>> string.digits

'0123456789'

上述范例,让我们见识到模组 string 里有个 split() 的物件方法,可以将一个字串变数值,依空白字元 (预设状况) 为切割点,分解成数个小字串,形成一个字串串列传回。如果切割条件不是空白字元时,在 split() 所接参数中予以指定,如 split(time, ':') 就是指定要以 ':' 字元为切割点。最后则是显示模组 string 有个字串变数 digits,内容设定为 '0123456789'。

如果我们想把上述字串串列里的‘数字’,如 '18' 与 '2001',由字串型别转换成数值型别,可以怎么做呢? 下列是个方法。

>>> def try_ai(s):

... if s[0] in string.digits:

... return string.atoi(s)

... else:

... return s

...

>>> import string

>>> date = "Fri May 18 CST 2001"

>>> piece = string.split(date)

>>> finish_ai = map(try_ai, piece)

>>> print finish_ai

['Fri', 'May', 18, 'CST', 2001]

>>>

首先,定义一个叫做 try_ai() 的函式,它在读进字串后,会比对字串的第一个字元,如果第一个字元是属于阿拉伯数字,那么就会尝试将字串转换成整数,最后传回其整数型别资料。是的,你会发现它的演算规则有些天真,不过,我们暂时还不需要一个无懈可击的转换函式。

接着,我们载入模组 string 之后,利用内建函式 map() 将自制函式 try_ai 与字串串列 piece 连结起来,如此一来,便能如愿将 piece 里的部份字串,转换成数值型别。显然 map() 函式在此例中帮上大忙,简洁地协助我们将自制函式与序列资料做了巧妙结合。

接下来,我们就可以进一步稍微改良原本天真的 try_ai() 函式。

>>> def try_ai(s):

... if ':' in s:

... ts = string.split(s, ':')

... return map(string.atoi,ts)

... if s[0] in string.digits:

... return string.atoi(s)

... else:

... return s

...

>>> import string

>>> date = "Fri May 18 12:03:27 CST 2001"

>>> piece = string.split(date)

>>> finish_ai = map(try_ai, piece)

>>> print finish_ai

['Fri', 'May', 18, [12, 3, 27], 'CST', 2001]

>>>

这个改良过的版本,可以进一步处理像 '12:03:27' 这样的‘数字’,否则原本更天真的版本会传回 ValueError 的错误讯息。

>>> piece = ['Fri', 'May', '18', '12:03:24', 'CST', '2001']

>>> def strp(x, y):

... return x + ' ' + y

...

>>> r = reduce(strp, piece)

>>> r

'Fri May 18 12:03:24 CST 2001'

>>>

上述程式片段,处理效果刚好与之前的程式相反,它会把字串串列重组成一个长字串。重点就是利用了内建函式 reduce(),其运作方式同样要输入一个函式名称及一个序数资料,不过,目的是要把序数资料的元素‘合并减少’成一个。reduce() 也可以应用在数值串列上,以下便是这样的范例。

>>> n = range(1, 11)

>>> def mult(x, y):

... return x * y

...

>>> f = reduce(mult, n)

>>> f

3628800

>>>

说穿了,它还是一个阶乘的范例,每呼叫一次 mult() 函式,数值串列的个数会越来越少,最后传回一个阶乘结果,在此例中,即 10! 的答案。

下列是一个简化版本的闰年判断程式,我们将介绍另一个函式 filter()。

>>> def leap(y):

... if (y%400) == 0:

... return 1

... elif (y%100) == 0:

... return 0

... elif (y%4) == 0:

... return 1

... return 0

...

>>> n = range(1900, 2001)

>>> leap_year = filter(leap, n)

>>> leap_year

[1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000]

>>>

函式 filter() 同样是接一个函式名称与一个序数资料为参数,重点在于,在自制函式中,你必须把‘想留下来的资料’,其函式传回值设为 1 (代表 true),而把‘不想留下来的资料’,其函式传回值设为 0 (代表 false)。如此一来,filter() 函式在依序处理完序数资料后,还是会传回一个序数资料,而且应该只留下你想要的资料。

lambda 表示式

lambda 表示式是个实用而重要的工具,其功能及本质还是函式,差别在于 lambda 没有‘明确的函式名称’,而且通常只用于简洁的函式叙述。

>>> n = range(1, 11)

>>> f = reduce(lambda x, y: x * y, n)

>>> f

3628800

>>>

又是熟悉的阶乘函式,有趣的是,它的表示方式非常简洁,乍看之下,初学者可能以为是天书了。lambda 表示式的语法如下:

lambda 参数串列: 表示式

例如 x + y、x * y、s[9] 都是表示式的例子。早期,lambda 表示式的概念是取自 Lisp 语言,使用上有其便利及优势,不过,对初学者而言,使用 lambda 表示式通常还是得花时间熟悉,若是‘画虎不成反类犬’,搞到程式大乱就得不偿失了。

相关说明

[1] 有人把缺少 return 叙述的‘函式’称为 procedure。本质上,由于函式不明确描述 return 的话,会自动以 None 值 (空型别) 传回,所以视之为函式并无不妥。

[2] Zope 是一套网站 Application Server 系统,完全由 Python 语言所开发出来,程式本身就是一个庞大的物件系统,有兴趣的朋友,可到网站 http://www.zope.org 观摩。

[3] 来点不一样的吧,欢迎访阅 Instant Python 网页,网址 http://www.hetland.org/python/instant-python.php

[4] 想让 Python 执行更有效率吗? 试试Python Performance Tips 网页,网址http://musi-cal.mojam.com/~skip/python/fastpython.html

[5] Python for Lisp Programmers 网页,网址 http://www.norvig.com/python-lisp.html

[6] A Comparison of Python and LISP 网页,网址http://www.strout.net/python/pythonvslisp.html

[7] A Python/Perl phrasebook 网页,网址http://starship.python.net/~da/jak/cookbook.html

[8] Major Python changes 网页,提供Python 改版增修重点说明,网址http://shell.rmi.net/~lutz/errata-python-changes.html

[9] Python Language Mapping 网页,网址http://www.informatik.hu-berlin.de/~loewis/python/pymap.htm      

作者: shiqiiang   发布时间: 2004-06-30