A Byte of Python

Swaroop C H

14 Jun 2013

A Byte of Python

《A Byte of Python》是一本介绍用Python语言编写程序的免费书。它为Python初学者提供指导或指南。如果你对计算机的知识仅限于知道如何保存文本文件,那么这本书非常适合你。

尽管当今Python 2仍被普遍使用,本书还是依据最新版Python 3编写的(更多请看Python 2 & 3 一节)。

《A Byte of Python》的读者对象?

下面是一些人对本书的评价:

我发现的最好的事,是我发现了《A Byte of Python》, 它是为初学者编写的一本杰出的书,它写的很好,通过不言而喻的例子对概念作了很好的解释。

-- Syed Talal (19岁)

这是我见到的最好的初学者指南!感谢你的努力。

-- Walt Michalik (wmich50-at-theramp-dot-net)

你做了我在网上发现的最好的Python指南,伟大的工作,谢谢!

-- Joshua Robin (joshrob-at-poczta-dot-onet-dot-pl)

为初学者做了Python编程的卓越介绍。

-- Shan Rajasekaran

嗨,我来自多米尼加共和国,我叫Pavel。最近,我读了你的《A Byte of Python》,我认为它是极好的!! :)。从例子中我学到了很多,你的书对像我这样的初学者有很大帮助...

-- Pavel Simo (pavel-dot-simo-at-gmail-dot-com)

我读完了《A Byte of Python》,我想我真的应该感谢你,当我读到最后,我感到非常难过,因为我不得不再回到无趣的、乏味的学习笔记等中去。不论如何,作为Python学习手册,我欣赏你的书。

-- Samuel Young (sy-one-three-seven-at-gmail-dot-com)

亲爱的Swaroop,我正跟着一个对教学没兴趣的老师上课。我们使用的是 O'Reilly 的《Python学习手册(第二版)》,它不是没有任何编程知识的初学者学习的教材,而且我们的老师也应该以另外一种方式教学。非常感谢您的书籍,如果没有它,我就不能学会Python和编程。一百万次地感谢!您把知识“掰开揉碎”到初学者能够理解的水平,这不是所有人都能做到的。

-- Joseph Duarte (jduarte1-at-cfl-dot-rr-dot-com)

我喜欢你的书!这是最好的Python教程,非常有用的参考手册,才华横溢,真正的杰作!请继续这种好的工作!

-- Chris-André Sommerseth

我给你发邮件,就是为了感谢你在线编写的《A Byte of Python 》。在偶然发现你的书之前,我已经尝试学习Python有几个月了。虽然我对pyGame取得了有限的成绩,但我从来没有完成过一个程序。

感谢你对分类的简单化,Python看起来确实是一个能够达到的目标。看起来我已经学会了基础知识,并能够继续我的真正目标——游戏开发。

...

再一次,非常感谢你将如此结构良好而且有用的编程基础教程放到网上,它帮助我彻底理解了OOP,这之前两本书都没行。

-- Matt Gallivan (m-underscore-gallivan12-at-hotmail-dot-com)

我感谢你以及你的《A Byte of Python》,我发现这是学习Python最好的途径。我现在15岁,住在埃及,我的名字叫Ahmed。Python是我学习的第二个编程语言,我在学校学习了Visual Basic 6,但我并不喜欢它,可是我真的喜欢学习Python。我成功地编写了地址簿程序。我打算尝试编写、阅读更多Python程序(如果你有意给我推荐一些有帮助的源代码)。我也将开始学习Java,如果你能告诉我在哪里可以找到向你的教程一样好的Java教程,那将对我有很大帮助。多谢。

-- Ahmed Mohammed (sedo-underscore-91-at-hotmail-dot-com)

初学者想更多的学习Python编程的最好的资源是Swaroop C H编写的110页的PDF教程《A Byte of Python》。这本书写得很好,跟随它学习很容易,或许是当前可以得到的最好的Python入门教程。

-- Drew Ames 在Linux.com上发表的文章Scripting Scribus中写道

昨天,我在Nokia N800上浏览了《A Byte of Python》的大部分内容,这是我至今遇到的最简明扼要的Python教程,极力推荐作为学习Python的一个起点。

-- Jason Delport 发表在博客

对我而言,@swaroopch 编写的《A Byte of Vim》和《A Byte of Python》是最棒的技术作品,读起来非常棒。#FeelGoodFactor

-- Surendran 在其微博上说

《A Byte of python》至今最好

(在回答“谁能推荐一个又好又便宜的学习Python基础的资源?”中答道)

-- Justin LoveTrue 在其Facebook 社区页面

“《A Byte of Python》非常有用,多谢 :)”

-- Chinmay

永远是对新手和有经验的程序员都适合的《A BYte of Python》的爱好者。

-- Patrick Harrington, 在StackOverflow回答

甚至 NASA 也这么说:
NASA甚至也使用这本书!在他们的喷气推进实验室的深空网项目中使用。

学术课程

本书正在或曾经在多个院校作为教材使用:

协议

本书基于Creative Commons Attribution-Share Alike 3.0 Unported协议。

这意味着:

请注意:

开始阅读

你可以在线阅读本书

购买本书

为了离线愉快阅读并支持本书的持续发展和改善,可以购买纸质印刷书籍

下载

* 如果您想支持本书的持续发展,请考虑购买本书*。

阅读本书的母语版

如果您对阅读或参与本书的翻译感兴趣,请看翻译。 # 前言

Python可能是为数不多的既简单又强大的几个编程语言之一。它对初学者和专家都很适合,更重要的是,用Python编程很有趣。本书目的是帮助你学习这个奇妙的语言,展示如何快速而方便地完成任务——事实上的“对编程问题的完美抗毒剂”。

读者对象

本书作为Python编程语言的指南或教程,主要面向初学者,同时对有经验的程序员也有帮助。

本书目的是,如果对于计算机,你只知道如何保存文本文件,那么你可以从本书学习Python。如果之前你有编程经验,那么你同样可以从本书学习Python。

如果您之前有过编程经验,你将对Python和你喜欢的编程语言之间的区别感兴趣——我高亮显示了这些区别。然而要提醒一点,Python将很快成为你最喜爱的编程语言!

教材历史

我为我编写的“Diamond”软件编写简化安装过程的安装程序时,我第一次开始使用Python。我不得不在Python还是Perl上绑定Qt库进行选择。我在网上做了一些研究,偶然发现了[Eric S. Raymond的一篇文章] (http://pythonology.org/success&story=esr), Raymond是一个著名的、值得尊敬的黑客。其中他谈道,Python是如何成为他最喜爱的编程语言的。我也发现PyQt的绑定比Perl-QT更加成熟。所以我决定选择Python。

然后,我开始搜索Python的优秀书籍。我没能找到一本!我确实找到了一些O'Reilly的书,但是它们要么太贵,要么更像是参考手册而不是教程。于是,我免强接受了Python的随机文档。但是它过于简单和小巧。它的确给出了关于Python的妙计,但是不完整。由于我有编程经验,因此我能够对付它,但它并不适合于初学者。

在我第一次使用Python六个月后,我安装了当时最新的Red Hat 9.0 Linux,开始使用KWord。我对它很兴奋,突然冒出一个想法,用它写一些关于Python的东西。我开始写了几页,但是很快就有30页之多。然后我认真地将其变成书的形式,使它更有用。经过几次重写,它已经达到了作为学习Python语言有用教程的水准。我将这本书作为我的贡献捐赠给开源社区。

本书开始于我在Python上的学习笔记,尽管为满足他人的口味,我做出了大量的努力,但直到现在我依然这么认为:

在开源的真正精神中,我收到了很多热心读者的建设性意见、批评和反馈,这些帮助我改进了本书。

本书的状态

应许多读者的要求,本书在2012年10月使用Pandoc重新编排,以便生成电子书文档,同时进行了错误修正以及更新。

2008年12月版本(从以前2005年3月的大修改)的修改是更新到Python 3.0。

本书需要像您这样的读者的帮助,指出任何不足、难以理解或者错误之处。请写信给主要作者 或者各个译者留下您的意见和建议。

官方网站

本书官方站点是,您可以在线阅读整本书、下载最新版本、购买纸质版本以及给我反馈。

要思考的一些事情

构建软件设计有两种途径:一种是足够简单以致明显没有缺陷,另一种是足够复杂以致没有明显缺陷。

-- C. A. R. Hoare

人生的成功,专注和坚持比天才和机会更重要。

-- C. W. Wendte

简介

Python是可以称得上即简单又功能强大的少有的语言中的一种。你将会惊喜地发现,专注于问题的解决方案而不是你正在使用的编程语言的语法以及结构,是多么容易。

官方对Python的介绍:

Python是一个易于学习的、功能强大的编程语言。它具有高效的高级数据结构和能够简单有效地实现面向对象编程。Python优美的语法和动态类型,连同解释型特性一起,使其在多个平台的许多领域都成为脚本处理以及快速应用开发的理想语言。

在下一章,我将更详细地讨论这些特性。

名字背后的故事
Python语言的发明人Guido van Rossum以BBC的喜剧《Monty Python's Flying Circus》给这个语言命名。他不是特别喜欢那些为了食物而杀死动物的蛇,这些蛇会用它们长长的身体缠绕住那些动物从而勒死它们。

Python的特点

简单

itself.Python是一门简单而文字简约的语言。阅读好的Python程序感觉就像阅读英语,尽管是非常严格的英语。Python的这种伪代码特性是其最大强项之一,它可让你专注于解决问题的办法而不是语言本身。

容易学习

正如你即将看到的,Python非常容易上手。就像刚刚提到的,Python具有格外简单的语法。

免费开源

Python是一个FLOSS(自由/自由与开源软件)的例子。在一些简单的条款之下,你可以自由地分发这个软件的拷贝,阅读其源代码,修改它,或者将其一部分用到新的自由程序中。FLOSS是基于共享知识社区的概念,这是Python如此好的原因之一——它是由那些希望看到更好的Python的社区创建和不断改进的。

高级语言

当你使用Python编写程序时,你永远不需要担心低级细节,比如你的程序管理内存的使用等。

可移植

基于其开放源代码的特性,Python已经被移植(也就是使其工作)到许多平台。只要你足够小心,避免使用系统相关特性,你的所有Python程序都可以不加修改地运行在这其中任意平台。

你可以在Linux、Windows、FreeBSD、Macintosh、Solaris、OS/2、Amiga、AROS、AS/400、BeOS、OS/390、z/OS、Palm OS、QNX、VMS、Psion、Acorn RISC OS、VxWorks、PlayStation、Sharp Zaurus、Windows CE,甚至PocketPC平台上使用Python。

你甚至可以使用类似Kivy平台为iOS(iPhone、iPad)和Android创建游戏。

解释型

这需要一些解释。

使用编译型语言(像C或者C++)编写的程序,会由编译器使用一系列标志和选项,将源代码(如C或者C++)转换成一种电脑能够识别的语言(二进制代码,也就是0和1)。在运行程序时,链接器/载入软件将程序从硬盘复制到内存,然后开始运行。

换句话说,Python不需要编译成二进制代码。你只需从源代码直接运行程序。在内部,Python将源代码转换成一种称为字节码的中间格式,然后将其翻译你的计算机的机器语言,然后开始运行。事实上,这一切都让Python的使用更为简单,因为你不必担心程序的编译、保证恰当的库被链接和载入等等。这也使得你的Python程序更易于移植,因为你只需要复制你的Python程序到另外一台计算机,然后它就可以工作了!

面向对象

Python同时支持面向过程和面向对象编程。在面向过程语言中,程序围绕着过程或者函数(只不过是可重复使用的程序片段)构建。在面向对象语言中,程序围绕着对象(数据和功能的组合)构建。Python具有非常强大但是过于简洁的执行面向对象编程的方式,特别是相对于C++或者Java这种大型语言来说。

可扩展

如果你需要一段运行很快的关键代码,或者是想要编写一些不愿开放的算法,你可以使用C或C++完成那部分程序,然后从你的Python程序中调用。

可嵌入

你可以将Python嵌入到C/C++程序,让你的程序的用户获得“脚本化”的能力。

扩展库

Python标准库的确很大。它能够帮助你完成许多工作,包括正则表达式、文档生成、单元测试、线程、数据库、网页浏览器、CGI(公共网关接口)、FTP(文件传输协议)、电子邮件、XML(可扩展标记语言)、XML-RPC(远程方法调用)、HTML(超文本标记语言)、WAV(音频格式)文件、加密、GUI(图形用户界面)以及其它系统相关的代码。记住,只要安装了Python,所有这些都能做到。这叫做Python的“遥控器”哲学。

除了标准库,还有各式各样的其它高质量库,你可以在Python包索引找到它们。

小结
Python的确是一个激动人心的功能强大的语言。Python那种性能和特性的恰到好处的组合让使用Python编程既有趣又简单。

Python 2 与 3

如果你不关心Python 2和Python 3的区别,可以跳过这一节。但是必须知道你所用的版本。

2008年,本书为Python 3重写过,是使用Python 3的最早的书籍之一。不幸的是,这对于那些读着本书Python 3版本,却在使用Python 2的读者感到困惑,反之亦然。但慢慢地,大家都转移到了Python 3。

所以,在本书中你将要学习使用Python 3,即使最终你还想用Python 2。记住,一旦你充分地理解或学习使用了其中的一个,你可以很容易学到两个版本之间的区别,然后很容易的适应。困难的是学习编程和理解Python语言的核心,这是本书的目标。一旦你达到这个目标,你可以根据自己的情形很容易的使用Python 2或Python 3。

关于Python 2和Python 3的详细区别,见Ubuntu wiki的Python/3页面

程序员说了些什么

或许你会对顶尖的黑客,比如ESR,怎么看待Python感兴趣:

  1. Eric S. Raymond,是《The Cathedral and the Bazaar》的作者,也是发明开放源代码这一术语的人。他说,Python已经成为他最喜欢的编程语言。这篇文章给我第一次关注Python的真正灵感。

  2. Bruce Eckel,是著名的《Thinking in Java》和《Thinking in C++》的作者。他说,没有什么语言能比Python更能令他高效。他说,Python或许是唯一让程序员工作更简单的一个语言。请看完整的采访

  3. Peter Norvig,是著名的Lisp的作者,Google搜索质量主管(感谢Guido van Rossum指出)。他说,Python一直是Google的主要部分。你可以通过查看Google Jobs验证这句话。这个页面上显示出,Python知识是招聘软件工程师的要求之一。

安装

在Windows上安装

访问下载最新版本。安装过程和其它基于Windows的软件类似。

警告
当您提示某些“可选”组件的时候,不要不选。

DOS提示符

如果你想要在Windows命名行,例如DOS提示符,使用Python,那么你需要正确设置PATH变量。

对于Windows 2000、XP、2003,点击控制面板---系统---高级---环境变量。在“系统变量”中点击PATH,选择编辑,然后在已有内容的最后部分添加;C:\Python33(请核实存在该文件夹,对于较新版本Python来说,文件夹的名字可能不同)。当然,要使用正确的目录名。

对于早期版本的Windows,打开C:\AUTOEXEC.BAT文件,添加一行“PATH=%PATH%;C:33”(不含引号),然后重启系统。对于Windows NT,使用AUTOEXEC.NT文件。

对于Windows Vista:

  1. 点击“开始”,选择“控制面板”。
  2. 点击“系统”,在右侧您将看到“查看计算机基本信息”。
  3. 左侧是一个任务列表,其最后一项是“高级系统设置”,点击它。
  4. 显示“系统属性”对话框“高级”选项卡。点击右下角的“环境变量”按钮。
  5. 在下方标题为“系统变量”框中,滚动“Path”,点击“编辑”按钮。
  6. 按需修改路径。
  7. 重启系统。除非重启,Vista不会意识到系统路径变量的修改。

对于Windows 7:

  1. 在桌面上右击“计算机”,选择“属性”;或点击“开始”,选择“控制面板”---“系统和安全”---“系统”,点击左侧的“高级系统设置”,然后选择“高级”选项卡。点击底部的“环境变量”按钮,在下方的“系统变量”中找到PATH变量,选中它点击“编辑”。
  2. 在变量值的最后,追加;C:\Python33
  3. 如果这个值是%SystemRoot%\system32;,它将变成%SystemRoot%\system32;C:\Python33
  4. 点击“确定”完成。不需要重启。

在Windows命令行上运行Python

对于Windows用户,如果正确设置了PATH变量你可以在命名行运行解释器。

要打开Windows终端,点击开始按钮,点击“运行”。在对话框输入cmd,按下回车键。

然后输入python3 -V,确保没有错误。

在Mac OS X上安装

对于Mac OS X用户,通过按Command+Space键打开终端(打开Spotlight搜索),输入Terminal然后回车。

安装Homebrew时运行:

ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"

然后安装Python 3 使用

brew install python3

现在,运行python3 -V,确保没有错误。

在Linux上安装

对于Linux用户,通过打开Terminal应用程序打开终端,或者按下Alt + F2,然后输入gnome-terminal。如果不成功,请参考文档或你所用Linux发行版的论坛。

下一步,我们需要安装python3包。例如,在Ubuntu上,可以使用sudo apt-get install python3。请参阅文档或是你安装的Linux发行版的论坛,寻找正确的包管理器运行。

一旦你完成安装,在shell运行python3 -V,在屏幕上你应该能够看到Python版本:

$ python3 -V
Python 3.3.0
注意

是shell的提示符,根据你电脑上的操作系统的设置会有所不同,因此我将使用$符号。

新的发行版默认安装?

新的发行版,例如Ubuntu 12.10将Python 3作为默认版本,所以,检查一下是不是已经安装了。

总结

现在开始,我们假设你已经在你的系统上安装好了Python 3。

接下来,我们将开始编写我们的第一个Python 3程序。

第一步

现在,我们将看到在Python中如何运行一个传统的“Hello World”程序。这将教你如何写、保存和运行Python程序。

使用Python运行你的程序有两种方法——使用交互式解释器提示符或使用一个源文件。现在,我们将看到如何使用这两种方法。

使用解释器提示符

在您的操作系统中打开终端(如前面安装章节所述),然后,输入“python3”按回车键,打开Python提示符。

一旦你启动python 3,您应该看到'>>>”,这被称为* Python解释器提示符*,你可以开始输入的东西。

在Python解释器提示符下,输入‘print(“Hello World”)’后按回车键。您应该看到输出了单词“Hello World”。

当使用一个Mac OS X计算机,下面是你将看到的一个例子。Python软件的细节会根据你的电脑不同而有所不同,但从提示符(即从“>>>”开始)与操作系统无关,应该是相同。

$ python3
Python 3.3.0 (default, Oct 22 2012, 12:20:36)
[GCC 4.2.1 Compatible Apple Clang 4.0 ((tags/Apple/clang-421.0.60))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print('hello world')
hello world
>>> 

注意,Python让你的代码行立即输出了!你刚才输入的是一个Python 语句。我们使用print打印出(不出所料)你提供给它的任何值。在这里,我们提供的是文本“Hello World”,并立即打印到屏幕上。

如何解释器提示符
如果你正在使用一个Linux或Unix shell,您可以通过按下“ctrl - d’或输入“exit()“(注意:记得包含括号,“()”),然后输入回车 键。如果您使用的是Windows命令行提示符,按“ctrl - z”键再按“回车”键,退出解释器提示符。

选择一个编辑器

我们不能在每次想要运行一些东西的时候都要在解释器提示符下输入我们的程序,所以我们必须把它们保存为文件,这样我们可以任意次地运行我们的程序。

要创建我们的Python源文件,我们需要一个可以输入并保存它们的编辑软件。一个优秀的程序员的编辑器将使你写源代码文件的生活更容易。因此,选择一个编辑器确实至关重要。你必须选择一个编辑器,就像你选择要买的汽车一样。一个好的编辑器会帮助您很容易地编写Python程序,(就像一辆车可以让你)以一个更快和更安全的方式,让你的旅程更舒适,并且可以帮助你达到你的目的地(实现你的目标)。

一个非常基本的需求是语法高亮显示,分别以不同的彩色显示你的Python程序所有的不同部分,以便您可以看到你的程序且使其运行可视化。

如果你不知道从哪里开始,我推荐可以在Windows、Mac OS X和Linux上使用的Komodo Edit软件。

如果您使用的是Windows,不要使用记事本——这是一个糟糕的选择,因为它不做语法高亮显示,而且更重要的是它不支持文字的缩进——之后我们在我们的例子中会看到,缩进是非常重要的。好的编辑器如Komodo Edit会自动地做到这一点。

如果你是一名有经验的程序员,那么你一定已经使用Vim或(Emacs)(http://www.gnu.org/software/emacs/)了。不用说,这是两个最强大的编辑器,使用它们来写你的Python程序,你会从中受益。就我自己而言,在我的大多数项目,甚至写一整本书都在用Vim。,从长远来看Vim或者Emacs是非常有用的,如果你愿意花时间去学习,那么我强烈建议你使用它们。然而,正如我之前提到的,初学者在这一刻,可以从 Komodo Edit开始集中学习Python而不是编辑器。

再次重申,请选择一个适当的编辑器,它可以使编写Python程序更有趣和更容易。

对Vim用户

John M Anderson有一个很好的如何[使Vim成为强大的Python IDE)(http://blog.sontek.net/blog/detail/turning-vim-into-a-modern-python-ide)的介绍。还推荐jedi-vim插件和我自己的dotvim配置

对Emacs用户

Pedro Kroger有一个很好的如何使Emacs成为强大的Python IDE的介绍。还建议BG的dotemacs配置

使用一个源文件

现在让我们回到编程。每当你学习一种新的编程语言时,有一个传统,你编写和运行的第一个程序是“Hello World”程序——当你运行它时,它所做的只是说“Hello World”。正如Simon Cozens(神奇的"Beginning Perl"的作者)所说,这是“向编程神祈求帮你更好学习语言的传统咒语。”

开始你选择的编辑器,输入以下程序并将其保存为“hello.py’。

如果你使用Komodo编辑器,点击File --- New --- New File,输入下行:

print('Hello World')

在Komodo编辑器,选File --- Save保存文件。

你应将文件保存在哪里?你知道位置的任何文件夹。如果你不明白这是什么意思,创建一个新文件夹,并使用该位置保存和运行你所有的Python程序:

使用'mkdir'命令在命令行创建一个文件夹,例如,“mkdir/tmp/py”。

重要的
总要确保你给它的文件扩展名是.py,例如,“foo.py”。

在Komodo Edit,请单击“Tools”---“Run Command”,输入"python3 hello.py",单击“Run”,你应该看到像下面截图的打印输出。

“Hello world”程序在Komodo编辑器中的截图
“Hello world”程序在Komodo编辑器中的截图

尽管最好的方式是在Komodo中输入它,但在命令行也可以:

  1. 打开在安装章节中介绍的一个命令行。
  2. Change directory(改变路径)到你保存文件的目录,例如cd /tmp/py
  3. 在命令行输入python3 hello.py命令运行程序。

输出如下所示:

$ python3 hello.py
Hello World

如果你得到了如上所示有输出,祝贺你!——你已经成功地运行了你的第一个Python程序。您已经成功地越过学习编程最难的部分--开始你的第一个程序!

如果你得到了一个错误,请完全输入如上所示程序,再次运行这个程序。注意,Python是区分大小写的,即“print”并不等于“Print”——注意,前者是小写字母“p”和后者是大写字母“P”。同样,确保每一行的第一个字母之前没有空格或制表符——之后我们将明白为什么这很重要

它是如何工作的

Python程序是由语句组成,在我们的第一个程序中,我们只有一个语句,在这个语句中,我们调用“print”函数,它只是打印文本“Hello World”。我们在(稍后章节](#函数)将详细学习函数——你现在应该理解的是,无论你在括号提供什么,都将打印到屏幕上。在本例中,我们提供了文本“Hello World”。

可执行的Python程序

这只适用于Linux和Unix用户,但Windows用户应该知道。

每次,你想要运行一个Python程序,我们必须显式地调用的“python3 foo.py”,但是我们为什么不能像运行我们电脑中的其它任何程序一样运行它呢?我们通过使用叫hashbang行代码可以实现。

添加下面的一行作为程序的第一行:

#!/usr/bin/env python3

这样,你的程序应该像现在这样:

#!/usr/bin/env python3
print('Hello World')

第二,我们必须给程序的可执行权限--使用' chmod '命令,然后运行源程序。

这里使用的chmod命令是change the mode of the file (改变文件的模式),通过给系统All(所有)用户execute(可执行)权限。

$ chmod a+x hello.py

现在,我们可以直接运行我们的程序,因为我们的操作系统调用“/usr/bin/env”,它按次序找到我们的Python 3软件,然后知道如何运行我们的源文件:

$ ./hello.py
Hello World

我们使用“./”用来指示程序在当前目录中。

为了让事情变得更有趣,你可以将文件重命名为“hello”然后像“./hello”这样运行它,因为系统知道它必须使用源代码文件中第一行指定位置的解释器运行它,所以它仍将工作。

到目前为止,我们已经能够运行我们的程序,只要我们知道确切的路径。如果我们希望能够运行该程序从文件夹?您可以通过将程序存储在列在“路径”环境变量中的一个文件夹中来实现。

无论你何时运行任何程序,系统在列在“PATH”(路径)环境变量中的每个文件夹查找该程序,然后运行程序。我们通过简单地复制这个源文件到“PATH”(路径)列出的目录中,可以让这个程序到处可用。

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/swaroop/bin
$ cp hello.py /home/swaroop/bin/hello
$ hello
Hello World

使用'echo'(回响)命令和变量(PATH)名称加前缀'$',指示shell我们需要“环境变量”的值,我们可以显示“PATH”(路径)变量的值。我们看到,“/home/swaroop/bin”是PATH变量中的一个目录,* swaroop *是在我的系统中我用的用户名,通常在你的系统中为你的用户名,会有一个类似的目录。

如果你想添加您所选择的目录到“PATH”变量中——这可以通过运行“export PATH=$PATH:/home/swaroop/mydir”,“:/home/swaroop/mydir”是我想添加到“PATH”变量中的目录。

如果你想写可以在任何时间、在任何地点运行的命令,这个方法非常有用。这就像创建你自己的在命令行使用的命令,就像“cd”或任何其他命令。

获得帮助

如果您需要快速获取任何的Python函数或语句的信息,那么您可以使用内置的“help”(帮助)功能。这是非常有用的,尤其是当使用翻译提示符时,例如,运行‘help(print)”——这将显示print函数的帮助--用于打印东西到屏幕上。

注意
q退出帮助

类似地,您可以获得Python中几乎任何事情的信息,使用“help()”去学习更多关于使用“help”本身的信息!

如果你需要获取操作符,如“return”的帮助,那么你只需要把这些放到引号内部,如“help('return'),所以,对于我们试图要做的事情,Python并不感到困惑。

总结

现在,你可以自由自在地编写、保存和运行Python程序了。

既然你是一名Python用户,让我们学习一些Python概念。 # 基础

只是打印“Hello World”是不够的,是吗?你想要做的不仅仅是这些,你想带一些输入,操纵它,得到些输出。在Python中,我们使用常量和变量可以实现这些。在这一章,我们还将学习一些其他的概念。

注释

注释是#符号右边的任何文字,它对读程序的人有很大注释用处。

例如:

print('Hello World') # 注意:print是一个函数

或者:

# 注意:print是一个函数
print('Hello World')

在你的程序中,你尽可能使用有用的注释:

代码告诉你怎样做,注释告诉你这样做的原因。

这对你的程序的读者是有用的,他们可以很容易地理解程序做什么。记住,六个月后这个人可以是你自己!

字面常量

字面常量的一个例子是一个数字就像“5”、“1.23”,或一个字符串像“这是一个字符串”或“它一个字符串!”。它被称为字面,因为它是字面的——你使用它的字面值。数字'2'总是代表本身并没有什么其他的——它是一个常数,因为它的价值是不能改变的,因此,所有这些被称为字面常量。

数字

数字主要有两种类型--整型和浮点数。

‘2’是整数的一个例子,它只是一个完整的数。

浮点数(或简称为浮点)的例子有3.2352.3E-4。符号E表示10的次方。在这种情况下,52.3E-4的意思是52.3 * 10-4`。

有经验的程序要注意:
Python中没有单独的long(长)整型。int(整型)可以是任意大小的整数。

字符串

字符串是字符的一个序列,字符串通常只是一串单词。

在你写的每一个Python程序中都要使用字符串,因此要注意以下部分:

单引号

你可以使用单引号例如'Quote me on this'指定字符串。所有的空白,例如空格和制表符都按原样保留。

双引号

在双引号中的字符串和在单引号中的字符串工作起来完全一样。例如"What's your name?"

三重引号

您可以使用三重引号-("""''')指定多行字符串。在三重引号中您可以自由使用单引号和双引号。例如:

'''This is a multi-line string. This is the first line.
This is the second line.
"What's your name?," I asked.
He said "Bond, James Bond."
'''

字符串是不可改变的

这意味着,一旦您已经创建了一个字符串,你就不能改变它。虽然这看起来似乎是一件坏事,但它真不是(坏事)。在我们后面看到的各种程序中,将会明白这不是一个限制。

C/C++程序员要注意

在Python中没有单独的“char”(字符型)数据。这里没有真正的需要它,我相信你不会错过它。

Perl/PHP程序员要注意

记住,单引号字符串和双引号字符串是相同的——他们不以任何方式不同。

格式方法

有时,我们可能想要从其它信息构建字符串。这就是“format()”方法有用之处。

保存下面几行到文件"str_format.py"中:

age = 20
name = 'Swaroop'

print('{0} was {1} years old when he wrote this book'.format(name, age))
print('Why is {0} playing with that python?'.format(name))

Output:

$ python3 str_format.py
Swaroop was 20 years old when he wrote this book
Why is Swaroop playing with that python?

它是如何工作的:

一个字符串可以使用特定的格式,随后调用format方法,用format方法替代那些使用适当参数的格式。

观察使用第一处,我们使用“{0}”对应于变量‘name’,这是format(格式)方法的第一个参数。类似的,第二个格式是“{1}”对应的“age”,这是格式方法的第二个参数。注意,Python从0开始计数,这意味着第一位置的索引是0,第二个位置的索引是1,等等。

注意,我们可以使用字符串的连接,name+'is'+str(age)+'years old' 实现同样的目的,但这非常讨厌、容易出错。第二,在这种情况下,通过format方法自动转换为字符串,而不是显式地转换为需要的字符串。第三,当使用的format 方法,我们可以改变消息,而无需处理使用的变量,反之亦然。

还要注意,这些数字(索引)都是可选的,所以你也可以写成:

age = 20
name = 'Swaroop'

print('{} was {} years old when he wrote this book'.format(name, age))
print('Why is {} playing with that python?'.format(name))

这将给与前面的程序相同的输出。

Python在format方法中做的是,用每个参数值替代规格的地方。这里有更详细的规格,如:

 decimal (.) precision of 3 for float '0.333'
>>> '{0:.3}'.format(1/3)
 fill with underscores (_) with the text centered
 (^) to 11 width '___hello___'
>>> '{0:_^11}'.format('hello')
 keyword-based 'Swaroop wrote A Byte of Python'
>>> '{name} wrote {book}'.format(name='Swaroop', book='A Byte of Python')

变量

仅仅使用字面常量会很快变得无聊——我们需要某种方式存储任何信息,同样操作它们。这就要引入变量。变量是它的名字所指示的东西——他们的值会有所不同。例如,你可以用变量存储任何值。变量是你电脑中存储信息的内存的一部分。不像字面常量,您需要某种方法来访问这些变量,因此你要给出他们的名字。

标识符命名

变量是标识符的一个示例。标识符是用来识别一些东西的名字。这里有一些你必须遵循的标识符命名规则:

数据类型

变量可以保存不同的被称为数据类型的数值。基本类型是数字和字符串,我们已经讨论了。在后面的章节中,我们将看到如何使用(类)(#面向对象的程序设计)创建自己的类型。

对象

记住,Python把程序中使用的任何东西作为一个对象。在常识中这意味着,我们说‘对象’,而不是说‘一些东西’,

面向对象编程的用户要注意
一切东西都是对象,包括数字、字符串和函数,在这个意义上讲,Python是坚定的面向对象的。

现在,我们将看到如何使用变量以及字面常量。保存下面的示例,运行这个程序。

怎样写Python程序

从此以后,保存和运行一个Python程序的标准过程如下:

  1. 打开选择的编辑器,例如Komodo Edit。
  2. 输入例子中给出的程序代码。
  3. 用提到的文件名保存为文件。
  4. 在命令行使用python3 program.py命令运行程序。

例如: 使用变量和常量

 Filename : var.py
i = 5
print(i)
i = i + 1
print(i)

s = '''这是一个多行字符串。
这是第2行。'''
print(s)

Output:

$ python3 var.py
5
6
这是一个多行字符串。
这是第2行。

它是如何工作的:

下面介绍这个程序如何工作的。首先,我们使用赋值运算符('=')为变量'i'指定了文字常量值‘5’,这一行叫做一个声明,因为它指出应该做一些事情,在这个例子中,我们将名为'i'的变量与值‘5’相联系。接下来,我们使用'print'函数打印'i'的值,不出所料,恰恰打印变量的值到屏幕上。

然后,我们给存储在变量'i'中的值加'1'后,再存回到'i'中。然后,我们把它打印出来,我我们想的一样,我们得到值'6'。

同样,我们为字符串变量's'指定了文字字符串,然后打印它。

静态语言程序员应注意
变量的使用只是通过给他们指定一个价。不需要/不使用声明或数据类型定义。

逻辑行与物理行

物理行是当你写程序时看到的一行。逻辑行是Python 看和的一个单独语句。Python默认一个物理行为一个逻辑行

一个逻辑行是一个语句,像print('Hello World')--如果它本身在一行上(像你在一个编辑器中看到的),那么,它也是一个物理行。

默认情况下,Python鼓励一行写一个语句的用法,这使代码更可读。

如果您想要在一个物理行列举多个逻辑行,那么您必须使用一个表示逻辑行/语句结束的分号(“;”)显式地指明。例如:

i = 5
print(i)

i = 5;
print(i);

等效。

同样可写成:

i = 5; print(i);

甚至是

i = 5; print(i)

然而,我强烈建议你坚持在每一个物理行编写一个最大的逻辑行。这就是你永远都不要使用分号。事实上,我从未使用,甚至在python程序中从来没有见过一个分号。

这个观念是很有用的,还有一种情况:如果你有一个长代码行,你可以通过使用反斜杠把它分解为多个物理行。这是被称为显式行连接:

s = '这是一个字符串。 \
这是字符串的继续。'
print(s)

输出结果为:

这是一个字符串。这是字符串的继续。

同样的,

print\
(i)

print(i)

相同

有时有一种隐含的假设,您不需要使用一个反斜杠。在这种情况下,逻辑行有一个开始圆括号、开始方括号或开始花括号,但不是一个结束的括号。这被称为隐式连接。当我们在以后的章节--编写程序使用列表时,你可以看到它的作用。

缩进

在Python中的空白是重要的。实际上,在一行开始的空格是重要的。这被称为缩进。在逻辑行开头的前导空白(空格和制表符)用于确定逻辑行的缩进级别,它用于依次确定语句的分组。

这意味着一起的语句必须有相同的缩进。每一个这样的语句组被称为。在后面的章节,我们将看到的块是何等重要的例子。

你应该记住的一件事是,错误的缩进可以产生错误。例如:

i = 5
 print('值是 ', i) # 错误! 注意在行的开头有一个空格
print('重复,值是 ', i)

当运行它时,将会发生下面的错误:

  File "whitespace.py", line 4
    print('Value is ', i) # Error! Notice a single space at the start of the line
    ^
IndentationError: unexpected indent

请注意,这里第二行的开头有一个空格。这个错误表明:Python告诉我们程序的语法是无效的,即程序写的不正确。这意味着,你不能任意开始语句中的新块(当然,除了默认的主块,您一直已经使用的)。您可以使用新块的情况,将在后面的章节详细,如(控制流](#控制流)。

如何缩进

缩进只使用空白,用制表符使用4个空格。好的编辑器如Komodo Edit会为你自动这样做。确保你使用一致的数量的缩进空格,否则你的程序将显示错误。

静态语言程序员应注意

Python为块总是使用缩进,从来不用花括号。运行from __future__ import braces可以了解更多。

小结

现在,我们已经经历了许多细节,我们可以转到更有趣的东西,如控制流语句。一定要熟悉这一章你所读的。 # 操作符和表达式

你编写的大多数语句(逻辑行)都将包含表达式。一个表达式的简单例子是2+3。一个表达式可分解成操作符和操作对象。

操作符 的功能是做一些事,通过符号,如+或特别的关键字表现。操作符需要一些数据来操作,这些数据被你作操作对象。在这个例子中23是操作对象。

操作对象

我们将简单地看一下操作符和它的用法:

注意,您可以使用交互式解释器计算例子中给出的表达式。例如,为了测试表达式“2 + 3”,使用交互式Python解释器提示符:

>>> 2 + 3
5
>>> 3 * 5
15
>>>
+ (加号)

两个对象相加

3 + 58. 'a' + 'b''ab'.

- (减号)

给出一个数减去另一数的差;如果缺少第一个操作数,它默认为是0。

-5.2 得到一个负数,50 - 2426.

* (乘法)

给出两个数的乘积或返回重复多次的字符串。

2 * 36. 'la' * 3 得到 'lalala'.

** (幂)

返回x的y次幂

3 ** 481 (也就是3*3*3*3)

/ (除法)

用y分x(x除以y)

4 / 31.3333333333333333.

// (整除)

得到除法的商

4 // 31.

% (取模)

返回除法的余数

8 % 32. -25.5 % 2.251.5.

<< (向左移位)

数字向左移动指定位数。(在内存中每个数字由比特或二进制数表示,例如:0和1)。 2 << 28. 2 用二进制表示为10

左移两位得到 1000,它表示数字8

>> (向右移位)

数字向右移动指定位数。

11 >> 15.

11 用二进制表示为1011,向右移动1位后得到二进制101,表示数字5

& (位与)

数字的位相与

5 & 31

| (位或)

数字的位相或

5 | 37

^ (位异或)

数字的位相异或

5 ^ 36

~ (位求反)

x的位求反结果为-(x+1)

~5-6.

< (小于)

返回x是否小于y。所有的比较运算符返回TrueFalse。注意这些名字的大小写。

5 &lt; 3 返回 False3 &lt; 5 返回 True.

比较运算符可以任意连接:3 &lt; 5 &lt; 7 返回 True.

> (大于)

: 返回x是否大于y

`5 &gt; 3` 返回 `True`。如果操作对象都是数字,它们首先转换为普通型,否则,将返回`False`。
<= (小于等于)

返回x是否小于等于y

x = 3; y = 6; x &lt;= y 返回 True.

>= (大于等于)

返回x是否大于等于y

x = 4; y = 3; x &gt;= 3 返回 True

== (等于)

比较操作对象是否相等

x = 2; y = 2; x == y 返回 True.

x = 'str'; y = 'stR'; x == y 返回 False.

x = 'str'; y = 'str'; x == y 返回 True.

!= (不等于)

比较操作对象是否不相等

x = 2; y = 3; x != y 返回 True.

not (逻辑非)

如果 x 是 True,它返回 False。如果 x 是 False,它返回 True

x = True; not x 返回 False.

and (逻辑与)

如果x是False, x and y 返回 False,否则它返回y的值。

x = False; y = True; x and y 返回 False,因为 x 为假。在这种情况下,Python将不计算y,因为它知道and左边表达式是 False ,这意味着整个表达式将为 False ,而不论其它值为什么。这叫做求值捷径。

or (逻辑或)

如果 x 为 True, 它返回真,否则它返回y的值。

x = True; y = False; x or y 返回 True。求值捷径这也适用。

数学操作和赋值的快捷方式

对一个变量进行数学操作是常见的。然后将操作的结果返回给变量,今后,对这样的表达式有一个快捷方式:

你可以把:

a = 2
a = a * 3

写成:

a = 2
a *= 3

注意:将 var = var operation expression 写成 var operation= expression

运算顺序

如果你有一个表达式如 2 + 3 * 4, 是先做加法还是先做乘法呢?我们的高中数学告诉我们,应该先做乘法。这意味着乘法操作符比加法操作符具有更高的优先级。

下面的表给出了Python运算顺序的优先表,从最低(最小约束力)到最高(最高约束力)。 意思是说,在给定的表达式中,Python将在计算表上方列出的对象和表达式之前,首先运算表下方的。

下面的表取自Python参考手册](http://docs.python.org/py3k/reference/expressions.html#summary),是为了提供完整性。为了显式地指定优先级,更好的做法是使用圆括号组织运算符和操作对象。这可使程序更加可读。详见下面更改运算顺序

lambda

Lambda表达式

or

逻辑或

and

: 逻辑与

not x

逻辑非

in, not in

成员检测

is, is not

鉴别检测

<, <=, >, >=, !=, ==

: 比较

|

位或

^

位异或

&

位与

<<, >>

移位

+, -

加和减

*, /, //, %

乘法,除法,浮点除和余数

+x, -x

正,负

~x

按位非

**

乘方

x.attribute

属性引用

x[index]

索引

x[index1:index2]

切片

f(arguments ...)

函数调用

(expressions, ...)

: 显示 Binding 或元组

[expressions, ...]

显示列表

{key:datum, ...}

显示字典

我们没有遇到的操作符将在后面的章节解释。

上表中在同一行列出的操作符具有相同优先级。例如,“+”和“-”具有相同的优先级。

改变运算顺序

为使表达式更具可读性,我们可以使用圆括号。例如2 + (3 * 4) 肯定比需要知道操作符运算优先级的 2 + 3 * 4 更容易理解。与其他方面一样,应该合理使用括号不应该冗余(不要过分使用),如(2 + (3 * 4))

使用括号有一个额外的优势——它帮助我们更改运算顺序。例如,如果您想要在一个表达式中加法在乘法之前运算,那么你可以这样写 (2 + 3) * 4

结合性

操作符通常从左到右。这意味着具有相同优先级的操作符从左到右的方式计算。例如2 + 3 + 4计算为 (2 + 3) + 4。一些操作符,像赋值操作符,有从右到左的结合性,即 a = b = c 被视为 a = (b = c)

表达式

例子 (保存为expression.py):

length = 5
breadth = 2

area = length * breadth
print('Area is', area)
print('Perimeter is', 2 * (length + breadth))

输出:

$ python3 expression.py
Area is 10
Perimeter is 14

它是如何工作的

矩形的长度和宽度以同样的名字存储在变量中,在表达式的帮助下,我们使用这些计算矩形的面积和周长。我们存储表达式length * breadth 的结果在变量area中,然后使用print函数打印它。在第二种情况下,在打印函数中我们直接使用表达式2 * (length + breadth)` 的值。

同样要注意,Python'完美打印'是如何输出的。即使我们没有在'Area is' 和变量area,之间指定一个空间,Python为我们得到一个干净漂亮的输出,而且这种方式使用程序的可读性更强(因为我们不需要担心为输入我们在字符串中间使用的空格)。这只是让Python程序员的生活更方便的一个例子。

小结

我们已经看到了如何使用操作符,操作对象和表达式——这是任何程序的基石。接下来,我们将看到在使用语句的程序中如何利用这些。 # 控制流

在程序中,到现在为止,我们看到,一直有一系列的语句被Python以由上而下的顺序如实地执行。如果你想改变它的流程,它会如何工作呢?例如,你想让程序作出一些决定,而且不同的情况做不同的事情,例如,根据一天的时间不同,打印“早上好”或“晚上好”?

正如你可能已经猜到的,这要通过使用控制流语句。在Python中有三个控制流语句-- if, forwhile

if 语句

if 语句是用来检查一个条件:如果条件为真,我们运行一个语句块(你为if块),(否则)else,我们执行另一个语句块(称为else块)。else子语句是可选的。

例如 (保存为 if.py):

number = 23
guess = int(input(`请输入一个整数: `))

if guess == number:
    print(`恭喜,你猜对了。`) # 新块从这里开始
    print(`(但你没有获得任何奖品!)`) # 新块在这里结束
elif guess < number:
    print(`不对,你猜的有点儿小`) # 另一个块
    # 在一个块中你可以做你想做的任何事...
else:
    print(`不对,你猜的有点大`)
    # 你猜的数比number大时才能到这里

print(`完成`)
# if语句执行完后,最后的语句总是被执行

输出:

$ python3 if.py
请输入一个整数: 50
不对,你猜的有点儿大
完成

$ python3 if.py
请输入一个整数: 22
不对,你猜的有点儿小
完成

$ python3 if.py
请输入一个整数: 23
恭喜,你猜对了。
 (但你没有获得任何奖品!)
完成

它是如何工作的:

在这个程序中,我们获取来自用户的猜测,并检查这个数是否是我们设定的数。我们给变量number设置我们想要的任何整数,比如 23。然后,我们使用input() 函数获取用户的猜的数。函数是可重用的程序块。我们在下一章中会阅读关于它们的更多东西。

我们给内置的input 函数提供一个字符串,该函数将其打印到屏幕上并等待用户输入。一旦我们输入一些东西并按下enter键,input()函数把我们的输入作为一个字符串返回。然后,我们使用int将这个字符串转换为整数,然后将其存储在变量guess中。实际上,int 是一个类,但现在所有你需要知道的是,您可以使用它来将一个字符串转变为一个整数(假设文本中的字符串包含一个有效的整数)。

接下来,我们比较用户猜的数和我们选择的数,如果他们相等,我们打印一条成功的消息。注意,我们使用缩进级别告诉Python语句属于哪个块。这就是为什么缩进P在ython中是如此重要。我希望你坚持"一致的缩进"的规则,好吗?

注意,if语句在最后有一个冒号——我们指示Python一个语句块将跟随其后。

然后,我们检查猜的数是否小于这个数字,如果是,我们通知用户,他们猜的数必须比那个数稍高。我们这里使用的是“elif”子句,实际上将两个相关的 if else-if else语句组合为一个语句if-elif-else,这使程序更简单且减少所需要的缩进。

elifelse语句也必须在逻辑行结束时有一个冒号,后跟相应的语句块(当然要通过适当的缩进)

你可以在if语句的if块中有另一个if语句——这称为if语句嵌套。

记住,elifelse部分是可选的。一个最小的有效的if语句是: python if True: print(是的,它为真)

在Python执行完成完整的if语句以及相关的elifelse子句,它移动到if包含语句的块中下一个语句块。在本例中,它是主要的块(程序开始执行的地方),接下来的语句是 print(完成)。在这之后,Python将看到程序的结尾,并简单的完成。

尽管这是一个非常简单的程序,但你应该注意,我已经指出很多东西。所有这些都是相当的直截了当(对那些有c/c++背景的人是惊人的简单)。最初,你需要意识到所有这些事情,但经过一些练习,对它们你将感到舒服,自然将是你所有的感觉。

C/C++程序员需要注意
在Python中没有switch语句。您可以使用一个if..elif..else语句做同样的事(和在某些情况下,使用词典去做更快速)

while语句

只要条件为真,while语句允许您多次执行一个语句块。while语句是被称为循环语句的一种。while语句可以有一个可选的else子句。

例如 (保存为while.py):

number = 23
running = True

while running:
    guess = int(input(`输入一个整数 : `))

    if guess == number:
        print(`恭喜,你猜对了。`)
        running = False # 这使while循环停止
    elif guess < number:
        print(`不对,你猜的有点儿小。`)
    else:
        print(`不对,你猜的有点儿大。`)
else:
    print(`while循环结束。`)
    # 在这做你想做的任何事

print(`完成`)

输出:

$ python3 while.py
输入一个整数 : 50
不对,你猜的有点儿大。
输入一个整数 : 22
不对,你猜的的点儿小。
输入一个整数 : 23
恭喜,你猜对了。
while循环结束。
完成

它是如何工作的:

在这个程序中,我们还是玩猜谜游戏,但优点在于,允许用户一直猜直到他猜对——每次猜测不需要重复运行该程序,正如我们在前一节中所做的。这演示了如何恰当的使用while语句。

我们移动inputif语句到while循环中,在while循环前,设置变量runningTrue。首先,我们检测变量running是否为True,然后往下执行相应的while块。在这个块执行完后,再检测条件,在这里是变量running,为真,我们再次执行while块,否则,我们执行可选的else块,然后执行下面的语句。

while循环的条件变为False时--这也可能发生在条件检测时的第一次,执行else块。如果在while循环中有else子句,它将一直执行,除非你使用break语句打破循环。

在这里TrueFalse被称为布尔类型,你可以认为它们分别相当于值10

C/C++程序员注意:
记住, while循环可以的else子句。

for循环

for..in语句是另一个循环语句,它迭代一个对象的序列,例如经历序列中的第一项。在后面的章节,我们将会看到更多关于序列的细节。现在,你需要知道的是一个序列只是一个有序的项目的集合。

例如 (保存为 for.py):

for i in range(1, 5):
    print(i)
else:
    print(`for循环结束`)

输出:

$ python3 for.py
1
2
3
4
for循环结束

它是如何工作的:

打印一个数字序列。我们使用内置的range函数生成这个数字序列。

我们在这里所做的是提供两个数字,range返回一个从第一个数字到第二个数字的一个数字序列。例如, range(1,5)给出序列[1, 2, 3, 4]。默认情况下,range 步距取为1。如果我们提供第三个数字,range那么它变成了步距。例如range(1,5,2)得到[1,3]。请记住,范围扩展第二号码,即它包括第二个数字。

注意,range()生成一个数字序列,当for循环请求下一个项目时,它一次只生成一个数字。如果你想立刻看到完整的数字序列,使用list(range())。list(列表)将在[数据结构章]中解释。

for循环然后遍历这个范围,for i in range(1,5)相当于 for i in [1, 2, 3, 4] 这就像把序列中的每一个数(或对象)分配给i,一次一个,然后为每个i值执行该语句块。在本例中,在语句块中我们只是打印它的值。

记住,else部分是可选的。当包括它时,除非遇到[break][#中断)语句,当for循环结束时,它执行一次。

记住,for..in循环可以作用于任何序列。在这里,我们对一个由内建的range函数生成的一个数字列表,但是一般来说,我们可以使用任何种类对象的任何类型的序列!在后面的章节,我们将详细探讨这个想法。

C/C++/Java/C#程序要注意

Python的for循环完全不同于C/c++的for循环。c#程序员会注意到,在Python中for循环类似于c中的foreach循环c#。Java程序员会注意到,同样类似于在Java 1.5中的to for (int i : IntArray)

在C/c++中,如果你想写for (int i = 0; i &lt; 5; i++),那么在Python中你只要写 for i in range(0,5)。正如您可以看到的,在Python中for循环更简单,更富有表现力且不易出错。

break语句

break语句是用来跳出一个循环语句,即停止执行一个循环语句,即使循环条件还没有成为False或序列的项目没有被完全遍历。

很重要的一点是,如果你跳出for或while循环,任何相应的循环else`块是执行的。

例子 (保存为break.py):

while True:
    s = input(`输入一些东西 : `)
    if s == `quit`:
        break
    print(`字符串的长度是`, len(s))
print(`完成`)

输出:

$ python3 break.py
输入一些东西: Programming is fun
字符串的长度是 18
输入一些东西: When the work is done
字符串的长度是 21
输入一些东西 : if you wanna make your work also fun:
字符串的长度是 37
输入一些东西 : use Python!
字符串的长度是 12
输入一些东西 : quit
完成

它是如何工作的:

在这个程序中,我们重复获取用户输入的东西并打印每次输入字符串的长度。我们提供一个特殊的条件--通过检查用户输入是否是 `quit来结束程序。我们通过跳出k循环停止程序,达到该程序的结束位置。

输入字符串的长度可以使用内建的len函数。

记住, break语句同样适用于for循环。

Swaroop 的诗意的Python

我在这里输入的是一个我写的、名为Swaroop 的诗意的Python:的迷你诗:

当工作完成时
编程是有趣的
如果你想让你的工作也很有趣:
    使用Python!

continue语句

continue语句是用来告诉Python跳过当前循环块中其余的语句,继续循环的下一次迭代。

例子 (保存为continue.py):

while True:
    s = input(`输入一些东西: `)
    if s == `quit`:
        break
    if len(s) < 3:
        print(`太小`)
        continue
    print(`输入的东西有足够的长度`)
    # 在这做其它各种处理...

输出:

$ python3 continue.py
输入一些东西: a
太小
输入一些东西: 12
太小
输入一些东西: abc
输入的东西有足够的长度
输入一些东西: quit

它是如何工作的:

在这个程序中,我们接受来自用户的输入,但是只有在输入的字符串至少有3个字符时才处理。因此,我们使用内建的len函数来获得长度,如果长度小于3,通过使用continue句,我们跳过块中的其余语句。否则,在循环中的其余语句将被执行--在这做其它各种处理。

注意,continue语句同样适用于for循环。

小结

我们已经看到了如何使用三个控制流语句--ifwhilefor以及与它们相关的 breakcontinue语句。这些都是Python最常用的部分,因此,变得对他们感到舒适是至关重要的。

接下来,我们将学习如何创建和使用函数。

函数

函数是重用的程序片段。它们允许你给一块语句一个名字,允许您在你的程序的任何地方使用指定的名字运行任何次数。这就是所谓的函数调用。我们已经使用了许多内置函数, 如lenrange

函数概念可能是任何有价值软件中重要的块(在任何编程语言中),所以,在这一章,我们将探索函数的各各方面。

定义函数使用def关键字。在这个关键字之后是标识函数的名字,其次是在一对括号中可以附上一些变量名,最后在行的末尾是冒号。接下来是语句块--函数的一部分。一个例子将展示这些,这实际上是非常简单的:

例子 (保存为function1.py):

def sayHello():
    print('世界您好!') # 属于函数的块
# End of function

sayHello() # 调用函数
sayHello() # 再次调用函数

输出:

$ python3 function1.py
世界您好! 
世界您好! 

它是如何工作的:

我们使用上述的语法,定义了一个称为sayHello函数。这个函数没有参数,因此没有在圆括号中声明变量。函数的参数只是输入到函数中,以便我们可以给它传递不同的值返回相应的结果。

注意,我们可以调用相同的函数两次,这意味着我们不需要再写同样的代码。

函数的参数

一个函数可以带参数--你提供给函数的值,利用这些值该函数可以一些事情。这些参数就像变量除了当我们调用函数时这些变量的值已经被定义,当函数运行时,它们已经有了指定的的值。

参数是在函数定义中在一对括号中指定,之间用逗号分隔。当我们调用这个函数,我们以同样的方式提供这些值。注意使用的术语——在函数的定义中给出的名字叫形参,而在函数调用时您提供的值被称为实参

例子 (保存为func_param.py):

def printMax(a, b):
    if a > b:
        print(a, '大')
    elif a == b:
        print(a, '等于', b)
    else:
        print(b, '大')

printMax(3, 4) # 直接给出字面值

x = 5
y = 7

printMax(x, y) # 给出参数的变量

输出:

$ python3 func_param.py
4 大
7 大

它如何工作的:

在这里,我们定义了一个称为printMax的函数,它使用叫做ab的两个参数。我们使用简单的if..else语句找出比较大的数,然后打印较大的数。

我们第一次调用函数printMax,我们直接提供了数字作为参数。在第二种情况下,我们调用函数使用变量作为参数。printMax把实参x分配给形参ay分配给b。在这两种情况下,printMax函数以同样方式工作。

局部变量

你在函数定义中声明的变量,他们与在函数外使用的其它同名变量没有任何关系,即变量名称对函数来说是局部的。这叫变量的范围。所有变量都有它们被声明的块的范围,从名称定义的点开始。

例子 (保存为func_local.py):

x = 50

def func(x):
    print('x等于', x)
    x = 2
    print('局部变量x改变为', x)

func(x)
print('x一直是', x)

输出:

$ python3 func_local.py
x等于50
局部变量x改变为2
x一直是50

它是如何工作的:

第一次,我们使用函数体中第一行打印变量x,Python使用在主块中,函数定义上声明的实参。

接下来,我们给x赋值为2,变量为x对我们的函数来说是局部变量,因此在函数中当我们改变x的值时,在主块中定义的变量x不受影响。

最后调用的print函数,显示在主块中定义的变量x,因此证实,它不受在前面调用函数的局部变量的影响。

使用全局声明

如果你想给在顶层的程序(即未在任何类型的范围如函数或类之中)定义的变量赋值,那么你必须告诉Python,变量不是局部的,而是全局的。我们使用global语句,没有global语句赋值给一个在函数外定义的变量是不可能的。

您可以使用这些在函数外定义的变量的值(假设在函数内没有同名的变量)。然而,这并不鼓励,应该避免,因为这使程序的读者不清楚变量是在哪里定义的,使用 global 语句就非常清楚,变量定义在一个最外的块中。

例子 (保存为func_global.py):

x = 50

def func():
    global x

    print('x的值是', x)
    x = 2
    print('全局变量x改为', x)

func()
print('x的值是', x)

输出:

$ python3 func_global.py
x的值是50
全局变量to改为2
x的值是2

它是如何工作的:

global语句用来声明x是全局变量,当我们在函数内给x赋值时,它的改变映射到我们在主块中使用的x的值。

用同样的global语句可以指定多个全局变量,比如: global x, y, z

默认参数

对于一些函数,你可能想要一些参数是可选,即在用户不希望为它们提供值时使用默认值,这在默认的参数值的帮助下完成的。你可以在函数定义中通过在参数名称后使用赋值操作符(=)后跟默认值来指定默认的参数值。

注意,默认参数值应该是一个常数。更准确的说,默认的参数值应该是不可变的——这在后面的章节中做了详细解释。现在,只要记住这点。

例子 (保存为 func_default.py):

def say(message, times = 1):
    print(message * times)

say('你好')
say('世界', 5)

输出:

$ python3 func_default.py
你好
世界世界世界世界世界

它是如何工作的:

函数say是用来按照指定的次数打印一个字符串。如果我们不提供一个值,那么在默认情况下字符串只打印一次。为此,我们为参数times指定一个默认参数值1

在第一次使用函数say时,我们只提供了字符串,它打印字符串一次。在第二次使用say时,我们提供了字符串和一个实参5两个参数,说明我们想要say字符串5次。

重要一点

只有在参数列表后面的的参数可以被赋予默认参数值,即在参数列表中,你不能在没有默认值的参数前有有默认参数值的参数。

这是因为,值按位置分配给参数。例如,def func(a, b=5)是有效的,而def func(a=5, b)无效的。

参数关键字

如果你有一些有许多参数的函数,您想要指定参数中的一些,那么,你可以通过为参数命名来为它们赋值——这叫做参数关键字——我们使用名称(关键字)而不是位置(我们一直使用的)来指定函数的参数。

这有两个优势,一是,使用函数容易,因为我们不需要担心参数的顺序。二是,如果其他参数有默认参数值,我们可以只给我们想赋值的参数赋值。

例子 (保存为func_key.py):

def func(a, b=5, c=10):
    print('a为', a, '和b为', b, '和c为', c)

func(3, 7)
func(25, c=24)
func(c=50, a=100)

输出:

$ python3 func_key.py
a为3 和b为7 和c为10
a为25 和b为5 和c为24
a为100 和b为5 和c为50

它是如何工作的:

名为func的函数中有一个参数没有默认参数值,后面两个参数有默认参数值。

第一次使用func(3, 7),参数a得到值3,参数b得到值7,参数c得到默认值10

第二次使用func(25, c=24), 参数a按照参数的位置得到值25,然后参数c按照参数名,也就是参数关键字,得到值24,变量b得到默认值5

第三次使用func(c=50, a=100),我们为所有给定的值使用了参数关键字。注意,我们为参数c指定值是在参数a之前,尽管在函数定义中ac前。

变量参数

有时你可能想定义一个函数,它可以获取参数的任何值,这可以通过使用星号(另存为total.py)实现:

def total(initial=5, *numbers, **keywords):
    count = initial
    for number in numbers:
        count += number
    for key in keywords:
        count += keywords[key]
    return count

print(total(10, 1, 2, 3, vegetables=50, fruits=100))

输出:

$ python3 total.py
166

它是如何工作的:

当我们声明一个星号的参数,如*param,那么从这一点开始到结束的所有位置的参数都被收集到一个叫param的元组中。

同样,当我们声明一个双星参数,如**param,那么人那一点开始到结束的所有关键字参数都被收集到一个叫param的字典中。

我们将在[后面章节)(#数据结构)中探讨元组和字典。

只有关键字的参数

如果我们想要指定特定的关键字参数作为只有关键字的参数,而是位置参数,它们可以被声明在星号参数后(另存为keyword_only.py):

def total(initial=5, *numbers, extra_number):
    count = initial
    for number in numbers:
        count += number
    count += extra_number
    print(count)

total(10, 1, 2, 3, extra_number=50)
total(10, 1, 2, 3)
# Raises error because we have not supplied a default argument value for 'extra_number'

输出:

$ python3 keyword_only.py
66
Traceback (most recent call last):
  File "keyword_only.py", line 12, in <module>
total(10, 1, 2, 3)
TypeError: total() needs keyword-only argument extra_number

它是如何工作的:

在星号参数后面声明参数,结果是只有关键字参数,如果这些参数没有提供一个默认值,那么如果关键字参数没有提供值,函数调用会产生错误,如上所示。

注意,+=的用法是操作符的缩写,因此,x = x + y,你可以写成x += y

如果你不需要星号参数,但还想使用只有关键字参数,那么只需简单使用一个星号而不使用任何名字,例如,def total(initial=5, *, extra_number)

return语句

return 语句用来从函数中return(返回),也就是说跳出函数。同样,我们也可以从函数中选择性地返回一个值

例子 (保存为func_return.py):

def maximum(x, y):
    if x > y:
        return x
    elif x == y:
        return '两个数相等'
    else:
        return y

print(maximum(2, 3))

输出:

$ python3 func_return.py
3

它是如何工作的:

函数maximum返回参数中的最大值,在这个例子中是提供给函数的数值。它使用了简单的if..else语句找到比较大的值,然后return(返回)那个值。

注意,没有一个值的return语句相当于return None(什么也不返回)None是Python中的一个特殊类型,它代表什么也没有。例如,如果一个变量的值是None,它说明这个变量没有值。

除非你已经写了自己的return语句,否则,每个函数都默认包含一个return None语句。通过运行print(someFunction())你可以看到这一点,这里someFunction 没有使用return语句,比如:

def someFunction():
    pass

在Python中pass语句用来说明一个空的语句块。

注意
已经有一个叫max的内建函数能够完成'find maximum'函数的功能 ,因此,只要可能使用这个内建函数。

文档字符串

Python有一个叫documentation strings(文档字符串)的好特性,通常用缩写名docstrings来指定。 文档字符串是你应该使用的一个重要工具,它对程序文档有助,令其容易理解。令人惊讶的是,当程序实际运行时,我们甚至可以从例如一个函数返回文档字符串。

例子 (保存为func_doc.py):

def printMax(x, y):
    '''打印两个数中的最大值。

    两个值必须是整数。'''
    x = int(x) # 如果可能,转换为整数
    y = int(y)

    if x > y:
        print(x, '最大')
    else:
        print(y, '最大')

printMax(3, 5)
print(printMax.__doc__)

输出:

$ python3 func_doc.py
5 最大
打印两个数中的最大值。

            两个值必须是整数。

它是如何工作的:

函数的第一个逻辑行的字符串是那个函数的文档字符串。注意,文档字符串也适用于在各自 的章节将要学习的模块和(类)(#面向对象的程序设计)。

文档的以贯例是多行字符串,第一行以大写字母开头以句点(.)结束(注:中文在V3.3中也可以),第二行是空行,从第三行开始是详细描述。强烈建议,为你重要的函数写文档字符串要遵循此贯例。

我们可以使用函数的__doc__(注意,双下划线)属性(属于名字的)访问printMax函数的文档字符串。只要记住,Python把一切任何事情作为一个对象对待,这也包括函数。我们将在这一章学习关于对象的更多知识。

如果你在Python中已经使用过help(),那么你已经看到如何使用文档字符串了!它所做的仅仅是获取函数的 __doc__ 属性,并以一个整洁的方式显示给你。你可以在上面的函数——在你的程序中仅包括help(printMax)尝试一下。记得按下q键,退出help

自动化工具可以从你的程序中以这种方式检索文档。因此,我强烈建议,为你写的任何重要函数使用文档字符串。来自Python的自动化工具pydoc命令使用文档字符串的工作原理类似于help()

小结

我们已经见过函数的很多方面,但请注意,我们仍然没有覆盖它们的所有方面。然而,我们已经覆盖了Python函数日常使用的大部分。

接下来,我们将看到如何创建及使用Python模块。

模块

您已经看到如何通过一次定义函数在程序中重用代码。如果你想在其它程序中重用一定数量的函数,你将写什么?正如你可能已经猜到了,答案是模块。

编写模块有各种各样的方法,但是最简单的方法是创建一个以.py 为扩展名、包含函数和变量的文件。

编写模块的另一种方式是使用编写Python解释器本身的本机语言,例如,你可以使用[C 编程语言)(http://docs.python.org/py3k/extending/index.html)编写模块,当它们被编译后,当使用标准的Python解释器时,在你Python代码中可以使用(这些模块)。

一个模块可以因另一个程序使用其功能而被imported(导入)。同样,我们可以使用Python标准库。首先 ,我们将看到如何使用标准库模块。

例子 (保存为 using_sys.py):

import sys

print('命令行参数是:')
for i in sys.argv:
    print(i)

print('\n\nPYTHONPATH在', sys.path, '\n')

Output:

$ python3 using_sys.py we are arguments
命令行参数是
using_sys.py
we
are
arguments

PYTHONPATH在[<nowiki>''</nowiki>, 'C:\\Windows\\system32\\python30.zip',
'C:\\Python30\\DLLs', 'C:\\Python30\\lib',
'C:\\Python30\\lib\\plat-win', 'C:\\Python30', 
'C:\\Python30\\lib\\site-packages']

它是如何工作的:

首先,我们使用import语句import导入sys(系统)模块。基本上,这意味着我们我们想告诉Python,我们想使用这个模块。sys模块包含了与Python解释器和其环境即system系统有关的函数。

当Python执行import sys 语句时,它查找sys模块。在这里,它是一个内建模块,因此,Python知道到去哪里找到它。

如果它不是一个编译的,也就是用Python写的模块,那么,Python解释器将在sys.path变量列表中的目录中搜索。如果模块被发现,那么,模块中的代码将运行,对你来说,使用模块变为有效。注意,初始化只有在我们第一次导入一个模块时完成。

sys模块中的argv变量是通过点符号访问的,例如,例如,sys.argv。它清楚地表明,这个名字是sys模块的一部分。这种方法的另一个优点是,这个名字与你的程序中使用的任何argv变量都不冲突。

sys.argv变量一个字符串list(列表)(list列表将在后面章节中详细解释)。具体来说,sys.argv包含命令行参数,也就是使用命令行向你的程序传递参数,的列表。

如果您正在使用IDE编写并运行这些程序,在菜单中寻找一种方法来指定命令行参数传递给你的程序。

这里,当我们执行python using_sys.py we are arguments时,我们使用 python命令和其后的传递给程序的参数运行using_sys.py模块。Python把命令行参数存储在 sys.argv变量中供我们使用。

记住,运行脚本的名字通常是sys.argv列表中的第一个参数。因此,在这里将有'using_sys.py'作为sys.argv[0]'we'作为sys.argv[1]'are'作为sys.argv[2]'arguments'作为sys.argv[3]。注意,Python从0而不是1开始数数。

sys.path包含被导入的模块所在的目录名列表。观察到sys.path就是的第一个字符串是空的——这个空字符串表示当前目录是和PYTHONPATH环境变量相同的、sys.path变量的一部分。这意味着你可以直接导入位于当前目录中的模块。否则,你将不得不把你的模块存放在sys.path列表中的一个目录中。

请注意,,当前目录是程序启动的目录。运行import os; print(os.getcwd())找到你的程序的当前目录。

字节编译的.pyc文件

导入一个模块是一个相对昂贵的事情,所以Python做 了一些技巧使它更快。一种方法是创建扩展名为.pyc字节编译文件,是Python将程序转换成的一种中间形式。(记得在Python如何工作的[简介部分)(#介绍))。当你下次从一个不同程序导入模块时,这种.pyc文件是有很用的--它将快得多,因为导入模块一部分需要的处理已经完成。同时,这些字节编译的文件是独立于平台的。

注意
这些.pyc文件通常在与之相应的.py文件的同一个目录中创建。如果Python在那个目录中没有写入权限,那么.pyc文件将不会创建。

from ... import语句

如果你想直接导入argv变量到程序中(为了避免每次为它键入sys.),那么您可以使用from sys import argv语句。

一般来说,你应该避免使用这个语句,而应该使用import语句,因为你的程序将避免名称冲突,将更具可读性。

例如:

from math import sqrt
print("16的平方根是", sqrt(16))

模块的name

每个模块都有一个名字,在模块中的语句能够找出它所在的模块的名字。这对于搞清楚模块是否正在运行或被导入这样的特殊用途是很方便的。正如前面提到的,当一个模块被第一次导入时,其所包含的代码被执行。我们可以通过使用这个,根据模块是否被自己使用或从另一个 模块被导入,使模块以不同的方式起作用,这些可以通过使用模块的 __name__属性来实现。

例子 (保存为 using_name.py):

if __name__ == '__main__':
    print('这个程序正在被自己运行')
else:
    print('我从别的模块被导入')

输出:

$ python3 using_name.py
这个程序正在被自己运行
$ python3
>>> import using_name
我从别的模块被导入
>>>

它是如何工作的:

每个Python模块有其__name__ 定义,如果是__name__ ,这意味着模块在被用户独立的运行,我们可以采取适当的行动。 ## 制作属于你自己的模块

创建自己的模块是很容易的,你一直在这样做,始终都是!这是因为每个Python程序也是一个模块。 你只需要确保它有一个.py扩展名。下面的例子会让你明白。

例子 (保存为mymodule.py):

def sayhi():
    print('嗨,这是我的模块在讲话。')

__version__ = '0.1'

上面的是模块的一个示例。正如您可以看到的,和我们通过的Python程序相比,没有什么特别的。接下来我们要看如何在我们的其它程序中使用这个模块。

记住,该模块要么放置在我们导入它的程序相同的目录中,要么放置在sys.path目录列表中的一个目录中。

另一个模块(保存为mymodule_demo.py):

import mymodule

mymodule.sayhi()
print ('版本', mymodule.__version__)

输出:

$ python3 mymodule_demo.py
嗨,这是我的模块在讲话。
版本 0.1

它是如何工作的:

注意,我们使用相同的点符号来访问模块的成员。Python充分重用相同的符号产生了独特的'神谕的'的感觉,这样我们不需要不断学习新的方法来做事情。

这是使用from..import语法的一个版本(保存为mymodule_demo2.py):

from mymodule import sayhi, __version__

sayhi()
print('版本', __version__)

mymodule_demo2.pymymodule_demo.py的输出相同。

注意,如果在导入模块中已经有一个__version__名字的声明,这里会有一个冲突。这也可能是因为它是常见的做法--对于每个模块使用这个名字声明它的版本号。因此,总是推荐选择import语句,虽然它可能让你的程序有点长。

你还可以使用:

from mymodule import *

这将导入所有的公共名称如 sayhi,但不会导入__version__,因为它始于双下划线。

Python的禅
Python的一个指导原则是"显式优于隐式"。运行import this去学习更多,看[栈溢出讨论)(http://stackoverflow.com/questions/228181/zen-of-python),那里列出了每个原则的例子。

dir函数

您可以使用内置的dir函数列出一个定义对象的标识符。例如,对于一个模块,包括在模块中定义的函数,类和变量。

当你给dir()提供一个模块名字时,它返回在那个模块中定义的名字的列表。当没有为其提供参数时, 它返回当前模块中定义的名字的列表。

例如:

$ python3

>>> import sys # 获得属性列表,在这里是sys模块的属性列表

>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__', '__s
tderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_compact_freelists',
'_current_frames', '_getframe', 'api_version', 'argv', 'builtin_module_names', '
byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle'
, 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable',
'exit', 'flags', 'float_info', 'getcheckinterval', 'getdefaultencoding', 'getfil
esystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof',
'gettrace', 'getwindowsversion', 'hexversion', 'intern', 'maxsize', 'maxunicode
', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platfor
m', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit
', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_in
fo', 'warnoptions', 'winver']

>>> dir() # 获得当前模块的属性列表
['__builtins__', '__doc__', '__name__', '__package__', 'sys']

>>> a = 5 # 创建了一个新变量 'a'

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'sys']

>>> del a # 删除/移除一个名字

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']

>>>

它是如何工作的:

首先,我们看到在导入sys模块上使用使用dir。我们能看到模块包含的巨大的属性列表。

然后,我们使用没有传递参数的dir函数。默认情况下,它返回模块的属性的列表。注意,导入模块的列表仍然是这个列表的一部分。

为了看到 dir在起作用,我们定义了一个新的变量,并为其赋值,然后检查dir,我们发现列表中添加了一个同名变量。我们使用del语句移除当前模块的变量或属性,在 del函数的输出中变化再次得到体现。

关于del的一点注意事项--这个语句用于删除一个变量/属性,语句运行后,这里是del a,你不能再访问变量a--就像它从来根本没有存在过。

注意,dir()函数对任何对象都起作用。例如,运行dir('print')来学习print函数的属性的更多知识,或运行dir(str)学习str类的属性的更多知识。

打包(封装)

现在,你必须开始观察组织你的程序的层次结构。变量通常在函数内部。函数和全局变量通常在模块内部。如果你想组织模块?这就到了牵涉到打包的地方了。

包只是模块的文件夹,使用一个特殊的__init__.py 文件,指示Python,这个文件夹是特殊的,因为它包含Python模块。

假设你想创建一个叫做'世界'的程序包,分装'亚洲'、'非洲'等等,分包按序包含'印度'、'马达加斯加'等等。

这是你将组织的文件夹: ~ - <在sys.path中现有的一些文件夹>/ - world/ - init.py - asia/ - init.py - india/ - init.py - foo.py - africa/ - init.py - madagascar/ - init.py - bar.py ~

包只是为了分层次组织模块的方便。在标准库中,你会看到包的许多实例。

小结

就像函数是可重用的部分的程序一样,模块也是可重用的程序。包是组织模块的另一个层次结构。来自Python的标准库,是包和模块的集合中的一个例子。

我们已经看到了如何使用这些模块和创建我们自己的模块。

接下来,我们将学习一些有趣的称为数据结构的概念。 # 数据结构

数据结构基本上是由于--他们是结构可将一些数据容纳在一起。换句话说,它们是用来存储一系列关的数据。

在Python中有四种 内建数据结构--列表、元组、字典和集合(set),我们将看到如何使用它们中的每一个,它们是怎样使我们的生活更容易的。

列表

列表是一种数据结构,它保存条目的有序集合。例如,你可以在列表中存储一个序列。这很容易想象,你想像一下购物清单,那里有你要购买物品的一个清单。除非在你的清单上每一行列有一个单独物品,然而,在python中,你在它们中间放上逗号。

条目的列表应包含在方括号内,以便Python明白你在指定一个列表。一旦您创建了一个列表,你可以添加、删除或是搜索列表中的条目。因为我们可以添加和删除条目,我们说一个列表是一个可变的数据类型,即这种类型可以更改。

对象和类的快速介绍

尽管直到现在,我一直推迟讨论对象和类,现在需要一个小小的解释,这样你可以更好的理解列表。我们将在后面章节中详细探讨这一课题。

列表是使用对象和类的一个例子。当我们使用一个变量 i,为它分配一个值,例如把整数5赋值给它,你可以认为它是创建一个int(即类型)的对象(即实例)i。事实上,你可以阅读的help(int)更好地理解这一点。

类也有方法,也就是为了使用而定义的只关于那个类的函数,只有当你有那个类的对象时,你才可以使用这些函数.例如,Python为list(列表)类提供了一个append方法,它允许你在列表的整改添加一个条目。例如,mylist.append('an item')将给列表mylist添加字符串。注意,我们使用点操作符访问对象的方法。

类也有字段,除了为了使用而定义的只与那个类相关的变量,它什么也没有。只有当你有那个类的对象时,你可以使用那些变量或名字。字段孔明通过点操作符访问的。例如,mylist.field

例子 (保存为using_list.py):

# 这是我的购物清单
shoplist = ['苹果', '芒果', '胡萝卜', '香蕉']

print('我要买', len(shoplist), '个物品。')

print('清单是:', end=' ')
for item in shoplist:
    print(item, end=' ')

print('\n我还要买大米。')
shoplist.append('大米')
print('现在我的清单是', shoplist)

print('现在我将要为我的清单排序')
shoplist.sort()
print('排序后的购物清单是', shoplist)

print('我要买的第一个物品是', shoplist[0])
olditem = shoplist[0]
del shoplist[0]
print('我已经买了', olditem)
print('现在我的清单是', shoplist)

输出:

$ python3 using_list.py
我要买 4 个物品。
清单是: 苹果 芒果 胡萝卜 香蕉
我还要买大米。
现在我的清单是 ['苹果', '芒果', '胡萝卜', '香蕉', '大米']
现在我将要为我的清单排序
排序后的购物清单是 ['大米', '胡萝卜', '芒果', '苹果', '香蕉']
我要买的第一个物品是 大米
我已经买了大米
现在我的清单是 ['胡萝卜', '芒果', '苹果', '香蕉']

(注:原文为英文,翻译为中文排序后结果与英文不一致,其后的运行结果也不一样了)

它是如何工作的:

变量shoplist是将要去超市的人的一个购物清单。在shoplist中,我们只存储了要买的物品的名字的字符串,你可以向清单中添加包括数字甚至是其它清单的任何对象

我们也使用了循环for..in遍历清单中的所有条目。到现在为止,你必须认识到一个清单也是一个序列。序列的特性将在后面的章节中讨论。

注意,在print函数中使用end关键字参数,表明输出以一个空格结束而不是通常的换行。

接下来,和前面讨论过的一样,我们使用列表对象的append方法向列表中添加一个项目。然后,我们只把列表简单地传递给print语句,整洁地打印列表的内容,以检查这个条目确实添加到了列表中。

然后,我们使用列表对象的sort方法为列表排序。 这个方法作用到列表本身,并不返回一个修改过的列表,理解这一点很很重要,它不同于对字符串的操作。这也是为什么我们列表是可修改的,而字符串是不可修改的原因。

然后,我们在超市购买了一个物品,我们想把它从购物清单中移除,通过使用del语句来实现。这里,我们提到我们想要移除清单中的哪个物品,del语句为我们将它从清单中移除。我们指定,我们想从清单移除第一项,因此,我们使用del shoplist[0](记住,Python从0开始数数数)。

如果你想知道列表对象定义的所有方法,详见help(list)

元组

元组是用来容纳多个对象。认为它们是类似于列表,但是没有列表给你的广泛功能。元组的一个主要特征是他们是不可变,像字符串,即您不能修改元组。

元组是通过在一对可选的圆括号中,项目之间用逗号分隔来定义的。

元组通常用在,一个语句或一个用户定义的函数能够安全地假设为值的集合,即值的元组,不会改变。

例子 (保存为using_tuple.py):

zoo = ('蟒蛇', '大象', '企鹅') # 记住圆括号是可选的
print('动物园中动物有数量有', len(zoo))

new_zoo = '猴子', '骆驼', zoo
print('在新动物园中笼子的数量是', len(new_zoo))
print('在新动物园所有的动物是', new_zoo)
print('从老动物园中带来的动物是', new_zoo[2])
print('从老动物园带来最后的动物是', new_zoo[2][2])
print('在新动物园中动物的数量有', len(new_zoo)-1+len(new_zoo[2]))

输出:

$ python3 using_tuple.py
动物园中动物有数量有 3
在新动物园中笼子的数量是 3
在新动物园所有的动物是 ('猴子', '骆驼', ('蟒蛇', '大象', '企鹅'))
从老动物园中带来的动物是 ('蟒蛇', '大象', '企鹅')
从老动物园带来最后的动物是 企鹅
在新动物园中动物的数量有 5

它是如何工作的:

变量zoo指的是一个物品的元组。我们看到len函数可以用来获取元组的长度。这也表明,一个元组同样也是一个(序列)(#序列)。

因为老动物园zoo将要关闭,我们现在将这些动物迁移到一个新的动物园new_zoo。因此,(新动物园)new_zoo的tuple包含一些已经存在的动物以及从老动物园zoo带来的动物。回到现实,请注意,在一个元组中的元组不失去其特性。

就像列表一样,我们可以通过在一对方括号中指定条目的位置,访问元组中的物品。这被称为索引操作符。我们通过指定new_zoo[2]访问新动物园new_zoo中的第三项,通过指定new_zoo[2][2]`访问新动物园new_zoo的第三项。一旦理解这个习语,这是非常简单的。

圆括号

尽管括号是可选的,我总是使用它们以便使它是一个元组更明显,特别是因为,它避免歧义。例如print(1,2,3)和print( (1,2,3) )`两件不同意思的事情——前者打印3个数,而后者输出一个元组(包含三个数字)。

有0个或1个条目的元组

一个空的元组由一对空的括号如myempty = ()组成。 然而,只有一个对象的元组并非如此简单。你必须通过在第一个对象(唯一的一个)后紧跟一个逗号来指定它,这样Python可以区分是一个元组还是一个表达式中一个对象的括号,例如,如果你想定义一个只含一个对象这为2的元组,你必须使用 singleton = (2 , )

Perl程序员应该注意

在一个列表中的列表不会失去其特性,也就是说并不像在Perl中夷为平地。这同样适用于在一个元组中的一个元组,或在一个列表中的元组,或在一个元组中的列表等。就Python而言,他们只是存储在另一个对象中的一个对象。

字典

字典就像一个地址簿,在那里你只通过知道他/她的名字,就可以找到地址或联系人详细信息。也就是说,我们使用键*(姓名)与值**(细节)相联系。注意,键必须是独一无二的,就像如果有两个完全相同的名字的人,你无法找到正确的信息。

注意,字典的关键字你只能使用不可变的对象(比如字符串),你可以使用不可变或可变的对象作为字典的值。这基本上意味着,简单地说,对于键你只能使用简单对象。

在字典中的一对键和值是通过使用冒号指定的,如,d = {key1 : value1, key2 : value2 }。注意,键值对用冒号分隔,彼此之间以逗号分隔,所有这些都是包含在一对大括号中。

记住,在字典中键-值对不以任何方式排序。如果你想要一个特定的顺序,那么你将不得不在使用前自己排序。

你将要使用的字典是dict类的对象或实例。

例子 (保存为 using_dict.py):

# 'ab'是英文address book(地址簿)的首个字母

ab = {  'Swaroop'   : 'swaroop@swaroopch.com',
        'Larry'     : 'larry@wall.org',
        'Matsumoto' : 'matz@ruby-lang.org',
        'Spammer'   : 'spammer@hotmail.com'
    }

print("Swaroop的地址是", ab['Swaroop'])

# 删除一个键-值对
del ab['Spammer']

print('\n地址薄中有 {0} 个联系人\n'.format(len(ab)))

for name, address in ab.items():
    print('联系人 {0} 的地址是 {1}'.format(name, address))

# 添加一个键-值对
ab['Guido'] = 'guido@python.org'

if 'Guido' in ab:
    print("\nGuido的地址是", ab['Guido'])

输出:

$ python3 using_dict.py
Swaroop的地址是 swaroop@swaroopch.com

地址薄中有 3 个联系人

联系人 Larry 的地址是 larry@wall.org
联系人 Matsumoto 的地址是 matz@ruby-lang.org
联系人 Swaroop 的地址是 swaroop@swaroopch.com

Guido的地址是 guido@python.org

它是如何工作的:

我们使用已经讨论过的符号创建字典ab。然后我们通过使用在列表和元组中讨论过的索引操作符--指定关键字来访问键-值对,遵守简单的语法。

我们可以使用我们的老朋友——del语句删除键值对,我们简单地指定字典和要删除的关键字的索引操作符,并将它传递给del语句。对于这个操作,没有必要知道对应于关键字的值。

接下来,我们我们使用字典的items方法,访问字典的每个键-值对的。它返回一个元组的列表,每个元组包含一对值--关键字及紧随其后的值。我们检索这对值并使用for..in循环为每一对分配给相应的变量nameaddress,然后在for块中打印这些值。

我们可以通过简单地使用索引操作符来访问一个键并分配值的方式添加新的键值对,像上面的例子中我们所做的添加Guido。

我们可以使用in操作符来检查一个键值对是否存在。

字典dict类的列表方法,请看help(dict)

关键字参数和字典
有一点不同需要注意,如果你在您已经在使用字典的函数中使用关键字参数,只是这样想,这个键值对是在函数定义的参数列表中指定的,而当你在函数中访问变量,它只是访问字典的一个键(在编译器设计术语中称为符号表)。

序列

列表、元组和字符串都序列的一个实例,但是什么是序列,它们为什么如此特殊呢 ?

主要特点是成员测试,(即in(在)和not in(不在)表达式中)和索引操作,这使我们在一个序列中能够直接获取一个特定的对象。

上面提到的——列表、元组和字符串这三种类型的序列,也有允许我们找回一彼序列即序列的一部分的切片操作。

例子 (保存为seq.py):

shoplist = ['苹果', '芒果', '胡萝卜', '香蕉']
name = 'swaroop'

# Indexing or 'Subscription' operation
print('第0项是', shoplist[0])
print('第1项是', shoplist[1])
print('第2项是', shoplist[2])
print('第3项是', shoplist[3])
print('第-1项是', shoplist[-1])
print('第-2项是', shoplist[-2])
print('第0个字符是', name[0])

# 一个列表的切片
print('第1项到第3项是', shoplist[1:3])
print('第2项到末尾是', shoplist[2:])
print('第1到-1项是', shoplist[1:-1])
print('开头到结尾是', shoplist[:])

# 字符串的切片
print('第1到第3个字符是', name[1:3])
print('第2到末尾的字符是', name[2:])
print('第1到-1的字符是', name[1:-1])
print('从头到尾的字符是', name[:])

输出:

$ python3 seq.py
第0项是 苹果
第1项是 芒果
第2项是 胡萝卜
第3项是 香蕉
第-1项是 香蕉
第-2项是 胡萝卜
第0个字符是 s
第1项到第3项是 ['芒果', '胡萝卜']
第2项到末尾是 ['胡萝卜', '香蕉']
第1到-1项是 ['芒果', '胡萝卜']
开头到结尾是 ['苹果', '芒果', '胡萝卜', '香蕉']
第1到第3个字符是 wa
第2到末尾的字符是 aroop
第1到-1的字符是 waroo
从头到尾的字符是 swaroop

它是如何工作的:

首先,我们看看如何使用索引来获得一个序列的个别项,这也称为订阅操作。当你在方括号中指定一个数字对应一个序列中的某项,如上所示,Python会为你取得序列中相对应位置的项。记住,Python从0开始数数。因此,在序列shoplist中, shoplist[0]取第一项和shoplist[3]获取第四项。

索引也可以是负数,在这种情况下,这个位置从序列的结尾开始计算。因此, shoplist[-1]指的是序列的最后一项, shoplist[-2]取倒数第二个项。

这个切片操作是通过指定序列的名称后面加上一个方括号,方括号中有一对可选的用冒号分隔的数。注意,这非常类似于你到现在一直使用的索引操作,记住这些数字是可选的但冒号不是。

在切片操作中的第一个数字(在冒号前)是切片开始的位置,第二个数字(在冒号后)是切片停止的位置。如果第一个数字没有指定,Python会从序列开头开始,如果第二个数字被冷落,Python会在序列的末尾停止。注意,返回的切片在开始位置开始,在结束位置前结束,也就是说,返回的切片包含开始位置,但不包含结束位置。

因此, shoplist[1:3] 返回序列的切片从位置1开始,包括位置2,但是在位置3停止,因此,返回两个项目的切片。同样,shoplist[:]返回整个序列的一个副本。

你也可以使用负位置做切片。负数用于从序列的结尾开始。例如,shoplist[:-1]` 将返回一个不包括序列最后一项,但包含了其它一切的切片。

你也可以为切片提供第三个参数,这是切片的步长(默认情况下,步长为1):

>>> shoplist = ['苹果', '芒果', '胡萝卜', '香蕉']
>>> shoplist[::1]
['苹果', '芒果', '胡萝卜', '香蕉']
>>> shoplist[::2]
['苹果', '胡萝卜']
>>> shoplist[::3]
['苹果', '香蕉']
>>> shoplist[::-1]
['香蕉', '胡萝卜', '芒果', '苹果']

注意,当步长是2时,我们获得位置0、2、……的项目,当步长是3晨,我们获得位置是0、3、等等的项目。

使用Python解释器的交互式提示,尝试指定切片的不同组合,以便你可以立刻看到结果。序列的一大好处是,你可以以同样的方式访问元组、列表和字符串!

集合

集合是简单对象的无序集合,用于一个集合中对象的存在比它的顺序或发生多少次更重要的时候。

使用集合,你可以测试成员,它是否是集合的子集以及找到两个集合的交集,等等。

>>> bri = set(['巴西', '俄罗斯', '印度'])
>>> '印度' in bri
True
>>> 'usa' in bri
False
>>> bric = bri.copy()
>>> bric.add('中国')
>>> bric.issuperset(bri)
True
>>> bri.remove('俄罗斯')
>>> bri & bric # 或者 bri.intersection(bric)
{'巴西', '印度'}

它是如何工作的:

这个例子是非常一目了然的,因为它涉及到学校教的数学的基本集合理论。

关联

当你创建一个对象,并赋给它一个值,该变量只是指向对象,并不代表对象本身!也就是说,变量名称指向你电脑中内存中的存储对象的那部分。这就是所谓的把名字绑定给对象。

一般来说,你不需要担心这个,但是对引用有一个需要你注意的微妙的影响::

Example (save as reference.py):

print('简单的分配')
shoplist = ['苹果', '芒果', '胡萝卜', '香蕉']
mylist = shoplist # mylist是指向同一对象的另一个名字!

del shoplist[0] # 我买到了第一项物品,因此我从清单中移除它

print('shoplist是', shoplist)
print('mylist是', mylist)
# 注意shoplist和mylist都打印没有‘苹果’的相同的清单
# 证明它们指向相同的对象

print('通过制作完整的切片复制')
mylist = shoplist[:] # 通过制作完整的切片复制
del mylist[0] # 移除第一项

print('shoplist是', shoplist)
print('mylist是', mylist)
# 注意,现在两个清单不同

输出:

$ python3 reference.py
简单的分配
shoplist是 ['芒果', '胡萝卜', '香蕉']
mylist是 ['芒果', '胡萝卜', '香蕉']
通过制作完整的切片复制
shoplist是 ['芒果', '胡萝卜', '香蕉']
mylist是 ['胡萝卜', '香蕉']

它是如何工作的:

在注释中有更多有用的解释。

记住,如果你想要复制一个列表或这种类型的序列或复杂的对象(而不是简单的对象如整数),那么您必须使用切片操作复制。如果你只是用另一个变量名指定,两个变量将“关联”到相同的对象,如果你不小心,这可能会引起麻烦。

Per程序员需要注意
记住,列表的一个赋值语句并创建一个副本。你必须使用切片操作复制序列。

关于字符串的更多

之前,我们已经详细讨论了字符串。在这能了解更多吗?嗯,你知道吗,字符串也是对象和也有做任何事情的方法--从检查的部分字符串到从字符串中分离。

在程序中你使用的字符串都是str类的对象,在下面的例子中将演示这个类的一些有用的方法,这些方法的完整列表,请看help(str)

例子 (保存为 str_methods.py):

name = 'Swaroop' # 这是一个字符串对象

if name.startswith('Swa'):
    print('是的,字符串以"Swa"开始')

if 'a' in name:
    print('是的,它包含字符串"a"')

if name.find('war') != -1:
    print('是的,它包含字符串"war"')

delimiter = '_*_'
mylist = ['巴西', '俄罗斯', '印度', '中国']
print(delimiter.join(mylist))

Output:

$ python3 str_methods.py
是的,字符串以"Swa"开始
是的,它包含字符串"a"
是的,它包含字符串"war"
巴西_*_俄罗斯_*_印度_*_中国

它是如何工作的:

在这里,我们看到字符串的很多方法在起作用。startswith方法是用来找出字符串是否以给定的字符串开始的。in操作符是用来检查一个给定的字符串是否是一个字符串的一部分。

find方法用于定位给定的子字符串在字符串内的位置,如果不能成功找到子字符串它返回-1。str类也有一个整洁的方法来连接一个序列的字符串,用充当分隔符的字符串连接序列中每个条目,返回一个由它生成的巨大的字符串。

小结

我们详细探索了Python各种内建的数据结构,写合理大小的程序,这些数据结构是至关重要的。

现在,我们有很多Python的基本知识已经到位,下面,我们看看如何设计和写一个真实的Python程序。 # 解决问题

我们已经探索过了Python语言的各种部分,现在我们通过设计和编写一个有用事情的程序,看一看如何将所有这些组合在一起,学习如何自己编写一个Python脚本可以实现这个想法。

问题

我们想要解决的问题是"我需要一个为我所有重要的程序创建备份的一个程序"

尽管这是一个简单的问题,但是我们没有着手解决这个问题的足够的信息。多一点的分析是必需的,例如,我们如何指定哪一个文件需要备份?他们是怎样存储的?

在得当的问题分析后,我们设计我们的程序。我们为程序如何工作列一个列表,在本例中,我创建了希望它如何工作的以下列表。如果你做这个设计,你可能不会拿出同样的分析,因为每个人都有自己做事的方式,这是非常好的。

解决方案

由于我们的程序的设计现在相当稳定,我们可以写实现解决方案的代码。

保存为backup_ver1.py:

import os
import time

# 1. 在列表中指出需要备份的文件和目录。
source = ['"C:\\My Documents"', 'C:\\Code']
# 注意我们在有空格的名字的字符串内不得不使用双引号。

# 2. 备份必须存储在一个主备份目录中。
target_dir = 'E:\\Backup' # 记住把它改为你要使用的目录

# 3. 备份的文件压缩到一个压缩文件中。
# 4. 压缩文件的名称是当前的日期和时间。
target = target_dir + os.sep + time.strftime('%Y%m%d%H%M%S') + '.zip'

# 5. 我们使用zip命令把文件压缩到一个压缩文件中
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))

# 运行备份
if os.system(zip_command) == 0:
    print('成功备份到', target)
else:
    print('备份失败')

Output:

$ python backup_ver1.py
成功备份到 E:\Backup\20080702185040.zip

现在,我们是在测试我们的程序能否正常工作的测试阶段。如果它不像预期的那样,则我们必须调试我们的程序,也就是从程序中去掉bug(错误)。

如果上面的程序不为你工作,在os.system调用之前放置一个print(zip_command),然后运行程序。 现在复制/粘贴print(zip_command)到shell提示符,看看它自己是否正常运行。如果这个命令失败,检查压缩命令手册,是什么可能是错的。如果这个命令成功,然后检查Python程序是否和上面的程序完全匹配。

它是如何工作的:

你会注意到,我们是如何以一个循序渐进的方式将我们的设计转换成代码的。

我们通过先导入和使用ostime模块,然后,我们在source清单中指定要备份的文件和目录,目标目录存储在变量 target_dir中,是我们要存储的所有的备份文件的地方,我们将要创建的压缩文件的名称是使用 time.strftime() 函数由当前日期和时间生成的。它也包含 .zip扩展名,将存储在target_dir目录中。

注意,变量os.sep的使用--目录的分隔符依你的操作系统而定,即,在Linux和 Unix中是'/',在Windows中是'\\',在Mac OS中是':'。使用os.sep而不是直接使用这些字符将使我们的程序更具可移植性,在所有这些系统上都能工作。

time.strftime()函数获取一个技术参数,像在上面的程序中我们已经使用过的。%Y参数将被替换为带着世纪的年份数字。%m参数将被一个位于0112的数字替换,如此等等。这种参数的完整列表可Python参考手册中找到。

我们创建目标文件的名称zip文件使用了加法操作符。加法操作符连接字符串,也就是,它将两个字符串连接在一起,并返回一个新的。然后,我们创建一个包含我们要执行的命令的字符串zip_command。你可以通过在shell(Linux终端或DOS提示符)运行来检查这个命令的工作。

我们使用了有一些选项并传递参数的zip命令。-q选项用于表明zip命令应该quietly(默默地)工作,-r选项指定zip命令应该rrecursively(递归地)工作,即它应该包括所有的子目录和文件。这两个选项组合在一起可缩写为-qr。要创建的压缩文件名后的选项后面紧跟要备份的文件和目录列表,我们使用字符串的join方法,这种方法我们已经知道如何使用,将source列表转换成一个字符串。

然后,我们终于使用os.system函数运行命令。os.system函数运行命令就仿佛在系统上也就是shell上运行它,如果命令运行成功,它返回0,否则返回一个错误号。

根据命令的结果,我们打印相应的消息,备份失败或成功。

就是这样,我们已经创建了一个备份我们重要文件的一个脚本!

Windows用户应注意
您还可以使用原始字符串,而不是双反斜杠转义序列,例如,使用'C:\\Documents'r'C:\Documents'。然而,不要使用'C:\Documents',因为你最终用一个未知的转义序列\D

现在,我们有一个能够工作的备份脚本,当我们想要备份文件时,我们可以用它。正如前面所讨论的,建议Linux/Unix用户使用(可执行方式)(#可执行的python程序),这样他们可以随时随地运行备份脚本。这被称为软件的操作阶段或部署阶段。

上面的程序正常工作,但(通常)第一个程序不会像你期望的那么样工作。例如,如果没有正确地设计程序或当键入代码时如果你犯了一个错误等,可能出现问题。相应地,你将不得不回到设计阶段或你需要调试您的程序。

第二版

第一个版本的脚本工作了。然而,我们还可以做一些改进,以便每天工作得更好。这被称为软件的维护阶段。

我觉得有用的改进之一是有一个更好的文件命名机制——在一个目录中,使用时间作为文件的名称,使用当前日期作为主备份目录中的一个目录。第一个优势是,你的备份以分层以方式存储,因此它更容易管理。第二个优势是,文件名短得多。第三个优势是单独的目录将帮助你检查每天是否创建了一个备份,如果某一天你备份了,将会创建一个目录。

保存为 backup_ver2.py:

import os
import time

# 1. 在列表中指出需要备份的文件和目录。
source = ['"C:\\My Documents"', 'C:\\Code']
# 注意,因为名字字符串中有空格,我们不得不使用双引号。

# 2. 备份必须存储在一个主备份目录中。
target_dir = 'E:\\Backup' # 记住把它改为你要使用的目录

# 3. 备份的文件压缩到一个压缩文件中。
# 4. 当前日期是主备份目录中子目录的名字
today = target_dir + os.sep + time.strftime('%Y%m%d')
# 当前时间是压缩文件的名字
now = time.strftime('%H%M%S')

# 如果子目录不存在,创建它
if not os.path.exists(today):
    os.mkdir(today) # 建立目录
    print('成功创建目录', today)

# 压缩文件的名字
target = today + os.sep + now + '.zip'

# 5. 我们使用zip命令把文件压缩到一个压缩文件中
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))

# 运行备份
if os.system(zip_command) == 0:
    print('成功备份到', target)
else:
    print('备份失败')

输出:

$ python3 backup_ver2.py
成功创建目录 E:\Backup\20080702
成功备份到 E:\Backup\20080702\202311.zip

$ python3 backup_ver2.py
成功备份到 E:\Backup\20080702\202325.zip

它是如何工作的:

大部分程序还保留了原样,变化是,我们使用os.path.exists函数检查在主备份目录中是否存在以当前日期为名字的目录,如果不存在,我们使用os.mkdir函数创建它。

第三版

当我们做一些备份时,第二版工作起来很好了。但当有许多备份时,我发现区分为什么备份是很困难的。例如,对一个程序或描述做一些重要的改变,然后我想知道这些变化与压缩文件的名字有什么联系。这个可以通过为压缩文件的名字附加上一个用户提供的注释而轻易实现。

注意
下面的程序不工作,所以不要惊慌,请继续,因为在这里有一个教训。

保存为 backup_ver3.py:

import os
import time

# 1.  在列表中指出需要备份的文件和目录。
source = ['"C:\\My Documents"', 'C:\\Code']
# 注意,因为名字字符串中有空格,我们不得不使用双引号。

# 2. 备份必须存储在一个主备份目录中。
target_dir = 'E:\\Backup' # 记住把它改为你要使用的目录

# 3. 备份的文件压缩到一个压缩文件中。
# 4. 当前日期是主备份目录中子目录的名字
today = target_dir + os.sep + time.strftime('%Y%m%d')
# 当前时间是压缩文件的名字
now = time.strftime('%H%M%S')

# 为创建一个压缩文件的名字从用户取得一个注释
comment = input('Enter a comment --> ')
if len(comment) == 0: # 检查是否输入注释
    target = today + os.sep + now + '.zip'
else:
    target = today + os.sep + now + '_' +
        comment.replace(' ', '_') + '.zip'

# 如果子目录不存在,创建它
if not os.path.exists(today):
    os.mkdir(today) # 建立目录
    print('成功创建目录', today)

# 5. 我们使用zip命令把文件压缩到一个压缩文件中
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))

# 运行备份
if os.system(zip_command) == 0:
    print('成功备份到', target)
else:
    print('备份失败')

输出:

$ python3 backup_ver3.py
  File "backup_ver3.py", line 25
    target = today + os.sep + now + '_' +
                                        ^
SyntaxError: invalid syntax

这怎么(不)工作:

这个程序不工作!Python说有语法错误这意味着脚本不满足Python预计的结构。当我们观察Python给出的错误,它还告诉我们它检测到错误的地方。所以我们从那一行开始调试我们的程序。

在仔细观察后,我们发现单一的逻辑行被分成两个物理行,但我们没有指定这两个物理行属于同一个逻辑行。基本上,Python发现在那个逻辑行添加操作符(+)没有任何操作对象,因此不知道如何继续。记住,我们可以通过在物理行的结束位置使用反斜杠指定当前行与下一物理行是连续的。所以,我们要改正我们的程序。我们找到错误时的这样修正叫做修复bug

第四版

保存为 backup_ver4.py:

import os
import time

# 1. 在列表中指出需要备份的文件和目录。
source = ['"C:\\My Documents"', 'C:\\Code']
# 注意,因为名字字符串中有空格,我们不得不使用双引号。

# 2. 备份必须存储在一个主备份目录中。
target_dir = 'E:\\Backup' #记住把它改为你要使用的目录

# 3. 备份的文件压缩到一个压缩文件中。
# 4. 当前日期是主备份目录中子目录的名字
today = target_dir + os.sep + time.strftime('%Y%m%d')
# 当前时间是压缩文件的名字
now = time.strftime('%H%M%S')

# 为创建一个压缩文件的名字从用户取得一个注释
comment = input('输入注释--> ')
if len(comment) == 0: # 检查是否输入注释
    target = today + os.sep + now + '.zip'
else:
    target = today + os.sep + now + '_' + \
        comment.replace(' ', '_') + '.zip'

# 如果子目录不存在,创建它
if not os.path.exists(today):
    os.mkdir(today) #  建立目录
    print('成功创建目录', today)

# 5. 我们使用zip命令把文件压缩到一个压缩文件中
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))

# 运行备份
if os.system(zip_command) == 0:
    print('成功备份到 ', target)
else:
    print('备份失败')

输出:

$ python3 backup_ver4.py
输入注释--> added new examples
成功备份到 E:\Backup\20080702\202836_added_new_examples.zip

$ python3 backup_ver4.py
输入注释-->
成功备份到 E:\Backup\20080702\202839.zip

它是如何工作的:

这个程序现在工作了!让我们仔细检查第三版的实际增强,我们使用input函数获取 用户的注解,然后通过使用len函数找到输入的长度检查用户确实输入了一些东西。如果用户只是按enter(回车键),没有输入任何东西(也许这只是一个常规备份或没有特殊的改变),那么,我们按照我们之前所做的处理。

然而,如果提供了一个注释,那么,它将附加到压缩文档名字中、 .zip扩展名前。请注意,我们将注释中的空格用开线正在取代空间在评论中用下划线——这是因为管理没有空格的文件名容易得多。

更细化

第四版对于大多数用户来说是一个令人满意的工作脚本,但总有改进的余地。例如,您可以为程序包括一个冗长级别,在那里你可以指定一个-v选项,从而使你的程序变得更加健谈。

另一个可能的优化处理是将允许额外的文件和目录被传递给该脚本的命令行。我们可从sys.argv列表得到这些文件名,我们可以使用list类提供的extend方法将它们添加到我们的source列表中。

最重要的改进是不使用的创建文档的os.system 方式,而是使用转而使用内建的 zipfiletarfile模块创建文档。他们是标准库的一部分,在你的计算机上已经为您提供使用没有外部依赖的压缩程序。

然而,在上面的例子中,纯粹是为教学的目的,我一直使用 os.system的方式创建一个备份,这样的例子对每个人的理解足够简单,但不是真正足够的有效。

你能使用zipfile(压缩文件)模块,而不是os.system调用尝试写第五版吗?

软件开发过程

我们已经经历了编写一个软件过程中的各种阶段。这些阶段可以概括如下:

  1. 什么 (分析)
  2. 怎样 (设计)
  3. 做 (实现)
  4. 测试 (测试和调试)
  5. 使用 (操作和部署)
  6. 维护 (优化)

编写程序的推荐方法是我们创建备份脚本的过程:做了分析和设计,开始实现用一个简单的版本,测试和调试它,来确保它能按预期工作。现在,添加你想要的任何功能,继续重复需要次数的做-试验循环。记住,软件是在成长,而不是建立

小结

我们已经看到了如何创建我们自己的Python程序/脚本和编写这种程序的不同阶段。你可能会发现创建你自己的程序就像我们在这一章做的是有用的,以便你熟悉Python以及解决问题。

接下来,我们将讨论面向对象编程。 # 面向对象编程

到现在为止,在我们编写的所有程序中,我们围绕着函数,也就是处理数据的语句块来设计我们的程序,这叫做面向过程的编程方式,还有一种组织你的程序的方式,是将数据和函数组合起来打包到称为对象的东西里面,这叫做面向对象编程技术。大多数情况下,你可以使用面向过程的编程方式,但当你编写大型程序或者有一些适用于这种方式更好的问题时,你可以使用面向对象的编程技术。

类和对象是面向对象编程的两个主要方面,一个创建一个新的类型,在这里对象是类的一个实例。一个比喻,你可以有int型变量,换句话说,存储整数的变量是int类的一个实例(对象)。

静态语言的程序员应该注意
注意,整数甚至被看作(int类的)对象。这不像在C++和(1.5版本以前的)Java语言中整数是原始的原生数据类型,关于类的更多细节,请看help(int)

C#Java 1.5程序员将发现这和装箱和拆封的概念相似。

对象可以使用属于对象的普通变量存储数据。属于一个对象或类的对象被称为字段。对象也可以通过使用属于类的函数有函数性。这样的函数被称为类的方法,这个术语是很重要的,因为它帮助我们区分函数和变量哪些是独立的,那些是属于一个类或对象的。总体而言,这些字段和方法可以被称为的属性。

字段有两种类型,它们可以属于类的每个实例/对象,或属于类本身。它们被分别称为实例变量类变量

要创建一个类使用class的关键字,类的字段和方法在一个缩进块中列出。

自我

类的方法与普通的函数只有一个特别的不同点--他们必须有一个额外的第一个名字、必须被添加到参数列表的开始处,但你调用该方法时,不用给此参数的值,Python将提供它。这个特别的变量指向对象本身,按照惯例,它的名字是self

虽然,你可以给这个参数任何名字,强烈推荐你使用名称self --任何其他的名字肯定是不清楚的。使用标准的名字,有许多优势--你的程序的任何读者将立即认出它,如果你使用self,甚至专门的ide(集成开发环境)也可以帮助你。

C++/Java/C#程序员要注意
在Python中,self相当于C++中的指针this、Java和C#中的this引用。

你一定很想知道Python怎样给self赋值,为什么你不需要给它一个值。一个例子会使这个清楚。假设,你有一个称为MyClass的类和这个类的实例称为myobject。当你调用这个对象的方法myobject.method(arg1, arg2)时,Python将自动转换成MyClass.method(myobject, arg1, arg2)--这是关于self的所有特殊之处。

这也意味着,如果你有一个不带任何参数的方法,那么你还得有一个参数——self

最简单的类可能是如下面的示例所示(另存为simplestclass.py).

class Person:
    pass # 一个空块

p = Person()
print(p)

输出:

$ python3 simplestclass.py
<__main__.Person object at 0x019F85F0>

它是如何工作的:

我们使用的类的声明和类的名称创建一个新的类,接下来是形成类的主体语句的一个缩进块。在这里,我们使用pass语句表示这是一个空的块。

接下来,我们使用类名后跟一对圆括号创建这个类的一个对象/实例(在接下来的部分,我们将学习更多关于实例化的知识)。为了验证,我们通过简单地打印它确认变量的类型。它告诉我们,在__main__模块中有一个Person类的实例。

注意,你的对象存储在计算机内存的地址也被打印了。因为Python找到任何地址就存储对象,因而,在你的计算机上地址会有所不同。

对象的方法

我们已经讨论了类/对象除了有额外的self变量外,还可以有方法,就像函数。现在,我们将看到一个例子(另存为”的方法py”)。

class Person:
    def sayHi(self):
        print('嗨,你好吗?')

p = Person()
p.sayHi()

# 这个小例子也可写成Person().sayHi()

输出:

$ python3 method.py
嗨,你好吗?

它是如何工作的:

在这里我们看到self在起作用。注意, sayHi方法不包含任何参数,但在函数定义中仍有 self

init 方法

在Python中有许多特别重要的方法名称,现在,我们看看__init__方法的重要性。

类的一个对象一被初始化, __init__方法就运行。这个方法对你的对象做任何初始化都是有用的。

例子 (保存为 class_init.py):

class Person:
    def __init__(self, name):
        self.name = name
    def sayHi(self):
        print('嗨,我的名字是', self.name)

p = Person('Swaroop')
p.sayHi()

# 这个小程序也可以写成 Person('Swaroop').sayHi()

输出:

~ $ python3 class_init.py 嗨,我的名字是 Swaroop~

它是如何工作的:

在这里,我们定义一个带参数name(和通常的 self)的__init__方法。在这里,我们只是创建一个新的称作name的字段。注意,尽管它们都叫 name,但它们是两个不同的变量。因为self.name中的点符号意味着"self"对象的一部分有个叫"name" 的东西,而另一个"name"是一个局部变量,因此没有问题。因为我们明确地表明我们所指的是哪个的名字,没有混乱。

最重要的是。请注意。我们没有显式地调用 __init__ 方法,而是当创建类的一个实例时,通过在类名称后的括号内传递参数,这是该方法的特殊意义。

现在,我们可以在我们的方法中使用self.name字段了,在sayHi方法中已经做了演示。

类和对象的变量

我们已经讨论了类与对象的部分功能(即方法),现在让我们了解一下数据部分。数据部分,即字段,只不过是被绑定到对象和类的空间名字的普通变量。这意味着,这些名字只有在类和对象的环境内有效。这就是为什么他们被叫做空间名字的原因。

有两种类型的字段--类变量和对象变量,它们的分类取决于类和对象分别属于哪种变量。

类变量是共享的——他们可以被该类的所有实例访问。类变量只是一个拷贝,当任何一个对象改变一个类变量时,所有的其它实例都将改变。

对象变量是类的每个对象或实例所特有的。既然这样,每个对象都有自己的字段拷贝,也就是说,在不同的实例中,它们不共享,同名的字段没有任何联系。一个例子能使你容易理解(保存为objvar.py):

class Robot:
    '''表示人一机器人,有一个名字。'''

    # 一个类变量,数机器人的数量
    population = 0
 
    def __init__(self, name):
        '''初始化数据。'''
        self.name = name
        print('(初始化 {0})'.format(self.name))
 
        # 当创建一个人时,机器人
        # 人口加1
        Robot.population += 1
 
    def __del__(self):
        '''我将要死了。'''
        print('{0} 正在被毁!'.format(self.name))
 
        Robot.population -= 1
 
        if Robot.population == 0:
            print('{0}是最后一个。'.format(self.name))
        else:
            print('还有{0:d}机器人在工作。'.format(Robot.population))
 
    def sayHi(self):
        '''机器人问候。
 
        是的,它们能做作那个。'''
        print('你好,我的主人叫我'.format(self.name))

    def howMany():
        '''打印当前人口。'''
        print('我们有{0:d}个机器人。'.format(Robot.population))
    howMany = staticmethod(howMany)
 
droid1 = Robot('R2-D2')
droid1.sayHi()
Robot.howMany()
 
droid2 = Robot('C-3PO')
droid2.sayHi()
Robot.howMany()
 
print("\n机器人在这能做一些工作。\n")

print("机器人已经完成了它们的工作,因此,让我们销毁它们。")
del droid1
del droid2

Robot.howMany()

输出:

$ python3 objvar.py
(初始化 R2-D2)
你好,我的主人叫我
我们有1个机器人。
(初始化 C-3PO)
你好,我的主人叫我
我们有2个机器人。

机器人在这能做一些工作。

机器人已经完成了它们的工作,因此,让我们销毁它们。
R2-D2 正在被毁!
还有1机器人在工作。
C-3PO 正在被毁!
C-3PO是最后一个。
我们有0个机器人。

它是如何工作的:

这是一个很长的例子,但有助于展示类和对象变量的特性。在这里,population 属于Robot类,因此是一个类变量。name变量属于对象(使用self分配),因此是一个对象变量。

因此,我们提到population类变量使用Robot.population 而不是self.population。我们在那个对象的中提到对象变量name使用self.name符号。记住对象和类变量的简单区别。还请注意,一个对象变量与一个类变量名字相同时,类变量将被隐藏!

howMany实际上是一个属于类而不是对象的方法,这意味着我们可以将其定义成 classmethodstaticmethod中的任何一个,这取决于我们是否需要知道是哪个类。因为,我们不需要这样的信息,我们主张staticmethod

我们也可以使用(decorator)(http://www.ibm.com/developerworks/linux/library/l-cpdecor.html)达到同样的目的:

@staticmethod
def howMany():
    '''打印当前人口。'''
    print('我们有{0:d}个机器人。'.format(Robot.population))

修饰符可以被想象为一个调用显式声明的捷径,正如我们在这个例子中已经看到的。

注意到,__init__方法用于使用一个名字初始化 Robot 实例。在这个方法中,因为还有一个机器人被添加,我们为population计数加了1。还发现,self.name的值是针对每一个对象的,这表明对象变量的特性。

记住,你必须只有使用self引用同一对象的变量和方法,这就是所谓的属性引用

在这个程序中,我们也看到了类和方法的文档字符串的用法。在运行时我们可能通过使用Robot.__doc__访问类的文档字符串,使用 Robot.sayHi.__doc__ 访问方法的为文档字符串。

就像__init__方法一样,还有一个特殊的方法__del__ ,该方法是在当对象将要毁灭时被调用,也就是说它不再被使用、能够为计算机系统释放出被它使用的内存。在这个方法中,我们简单地给 Robot.population 减1。

del`方法在对象不再使用时使用,当这个方法被运行时没有保证。如果你想明确地看到它在起作用,我们所能做的是必须使用del语句。

所有的类成员是公共的,一个例外是:如果你使用的数据成员的名字使用了双下划线前缀__privatevar, Python使用名称改编来有效地使它成为一个私有变量。

因此,下面的惯例是,只在对象和类中使用的任何变量,首先应该以一个下划线开始,其他所有的名字都是公共的,且可以被用于其他的类/对象使用。记住,这只是一个惯例和不是被Python强制执行的(除了双下划线前缀)。

Note for C++/Java/C# Programmers
在Python中,所有类成员(包括数据成员)是公共有和所有的方法是虚拟

继承

面向对象编程的一个好处是代码的重用,一种方式是通过继承机制实现,继承可以被想像为实现类之间的一种类型和子类型的关系。

假设您想编写一个大学里教师和学生记录的程序,他们有一些共同的特性,如姓名、年龄和地址。他们也有特定的特性,如老师的工资、课程和树叶和学生的学费、分数。

您可以为每个类型创建两个独立的类,并且处理它们,但要添加一个新的共同特征意味着要在这两种独立的类中都要添加,很快就会变得难以处理。

一个更好的方法是创建一个共同的类称为 SchoolMember ,然后从这个类继承老师类和学生类,也就是说它们成为这个类的子类,可以对这些子类添加特定的特征。

这种方式有很多优点,如果我们在SchoolMember中添加/更改任何功能,在子类中会自动反映出来。例如,您可以为学生和老师添加一个新的身份证字段,可能通过直接把它们添加到SchoolMember类中来实现。然而,子类中的变化不影响其他子类。另一个优点是,如果你引用SchoolMember类的一个老师或学生对象,在某些情况下如计算学校成员的数量时会很有用。这就是所谓的多态性,如果父类是预期的,子类在任何情况下可以被取代,即对象可以当做父类的一个实例。

还观察到,我们重用父类的代码,在不同的类中我们不需要重复,而在使用独立的类的情况下我们不得不重复。

在这种情况下,SchoolMember类被称为基类超类TeacherStudent类被称为派生类子类

现在,我们将看到作为程序的这个例子(保存为 inherit.py):

class SchoolMember:
    '''代表任何学校成员。'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('(初始化学校成员: {0})'.format(self.name))
    
    def tell(self):
        '''告诉我细节。'''
        print('Name:"{0}" Age:"{1}"'.format(self.name, self.age), end=" ")

class Teacher(SchoolMember):
    '''代表老师。'''
    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)
        self.salary = salary
        print('(初始化老师: {0})'.format(self.name))

    def tell(self):
        SchoolMember.tell(self)
        print('Salary: "{0:d}"'.format(self.salary))

class Student(SchoolMember):
    '''代表学生。'''
    def __init__(self, name, age, marks):
        SchoolMember.__init__(self, name, age)
        self.marks = marks
        print('(初始化学生: {0})'.format(self.name))
    
    def tell(self):
        SchoolMember.tell(self)
        print('Marks: "{0:d}"'.format(self.marks))

t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 25, 75)

print() # 打印一个空行

members = [t, s]
for member in members:
    member.tell() # 为Teachers和Students工作

输出:

~ $ python3 inherit.py (初始化学校成员: Mrs. Shrividya) (初始化老师: Mrs. Shrividya) (初始化学校成员: Swaroop) (初始化学生: Swaroop)

Name:"Mrs. Shrividya" Age:"40" Salary: "30000" Name:"Swaroop" Age:"25" Marks: "75"~

它是如何工作的:

使用继承,在类定义中,在类的名称后,我们在元组中指定基类名称,接下来,我们观察到使用 self变量,显式地调用基类的 __init__方法,这样我们可以初始化对象的基类部分。这是非常重要的,记住——Python不会自动调用基类的构造函数,您自己必须显式地调用它。

我们还观察到,我们可以在类名前加前缀调用基类的方法,然后和其它参数一道传递给 self变量值。

注意,当我们使用SchoolMember类的tell方法时,我们可以把TeacherStudent的实例作为SchoolMember的实例。

同时,观察到子类的tell方法的调用,不是SchoolMember类的tell方法。要理解这一点的一种方法是,Python 总是在实际的类型中开始寻找方法,如本例。如果它不能找到方法,它开始按在类定义中元组中指定的顺序一个接一个地查找属于它的基类的方法。

术语提示--如果在继承元组中不止列出一个类,那么它被称为多重继承

tell()方法中,end参数是用于来将换行变为在 print()调用结束后以空格开始。

小结

我们已经探讨了类和对象的各个方面以及与之关联的各种术语。我们也看到了面向对象编程的好处和缺陷。Python是高度面向对象,从长远看仔细理解这些概念仔细将对你很有帮助。

接下,我们将学习如何处理输入/输出和如何在Python中访问文件。 # 输入 输出

会有这种情况,你的程序必须与用户进行交互。例如,你想获取来自用户的输入,然后打印一些返回的结果。我们可以分别使用input()print()函数来实现。

对于输出,我们还可以使用str(字符串)类的各种方法。例如,您可以使用rjust方法来获取一个指定宽度的字符串。更多细节,见 help(str)

另一个常见的输入/输出类型是处理文件。创建、读和写文件是许多程序至关重要的,我们将在本章探讨这方面。

用户输入

将这个程序保存为 user_input.py:

def reverse(text):
    return text[::-1]

def is_palindrome(text):
    return text == reverse(text)

something = input('输入文本: ')
if (is_palindrome(something)):
    print("是的,这是回文")
else:
    print("不,这不是回文")

输出:

输入文本: 蜜蜂
不,这不是回文
输入文本: 人上人
是的,这是回文

它是如何工作的:

我们使用切片特性来颠倒文本。我们已经看到使用seq[a:b]代码获取从ab来自序列的切片。我们还可以提供一个第三个确定步长的参数,切片默认的步长是 1,它返回一个连续文本的一部分。给一个负的步长,即 -1 ,将以反向返回文本。

input()函数将一个字符串作为参数,并显示给用户。然后等待用户输入和按回车键。一旦用户输入和按下回车键,input()函数将返回用户输入的文本。

我们获取文本并颠倒它。如果原始文本和颠倒的文本是相等的,那么那个文本是一个回文

家庭作业

检查一个文本是否是一个回文应该忽略标点符号、空格和案例。例如,"Rise to vote, sir." 也是一个回文,但我们当前的程序并没有说它是。你能改善上述程序来识别这个回文吗?

下面的提示(不要读)

使用一个元组(从这里(http://grammar.ccc.commnet.edu/grammar/marks/marks.htm)你可以找到所有标点符号的一个列表)来保存所有的禁止字符,然后使用会员测试,以确定是否应该删除一个字符,即forbidden = ('!', '?', '.', ...)。

文件

为了读写,你可以通过创建一个file类的对象,分别使用readreadlinewrite方法来,打开和使用文件。能够读取或写入文件取决于文件打开时指定的模式。最后,当你完成对文件的操作时,你要调用close方法告诉Python,文件我们使用完了。

例子 (保存为 using_file.py):

poem = '''\
当工作完成时
编程是有趣的
如果想让你的工作有趣
    使用Python!
'''
 
f = open('poem.txt', 'w') # 为'写w'打开文件
f.write(poem) # 文本写入文件
f.close() # 关闭文件
 
f = open('poem.txt') # 如果不指定打开模式,默认为'读'
while True:
    line = f.readline()
    if len(line) == 0: # 0长度表示文件结尾
        break
    print(line, end='')
f.close() # 关闭文件

输出:

$ python3 using_file.py
当工作完成时
编程是有趣的
如果想让你的工作有趣
    使用Python!

它是如何工作的:

首先,通过内置的函数open,指定文件名和我们要打开的模式,打开一个文件。模式可以是读模式('r'), 写模式('w')或追加模式('a')。我们也可以指定是否以文本格式('t') 或二进制格式('b')读,写或追加。实际上有更多可用的模式,help(open) 会给你更多的细节。默认情况下,open()认为是一个以读方式打开的文本格式的文件。

在我们的例子中,我们首先以写文本格式打开文件,使用文件对象的write方法写文件,然后,我们最后 close(关闭)文件。

接下来,为再次阅读,我们打开同一个文件。我们不需要指定一个模式,因为 '读文本文件' 是默认的模式。我们使用readline方法在一个循环中每次读文件的一行。该方法返回一个完整的行,包括换行符结束时的行。当返回一个字符串时,这意味着我们已经到达文件的末尾,我们'打破'循环。

在默认情况下,print()函数在屏幕上自动换行打印文本。我们是通过指定end=''禁止产生新行,因为从文件读取的行在结尾已经包含一个换行符。然后,我们最终close文件。

现在,检查poem.txt的内容,确认程序确实写入和从那个文件读取。

拾取

Python提供了一个标准的模块称为pickle,使用它你可以在一个文件中存储任何的Python对象,然后把它弄回来后,这就是所谓的持续的存储对象。

例子 (保存为 pickling.py):

import pickle
 
# 我们将要存储对象的文件名
shoplistfile = 'shoplist.data'
# 购物清单
shoplist = ['苹果', '芒果', '胡萝卜']
 
# 定到文件
f = open(shoplistfile, 'wb')
pickle.dump(shoplist, f) # 把对象倒入一个文件
f.close()
 
del shoplist # 释放shoplist变量
 
# 从仓库读回
f = open(shoplistfile, 'rb')
storedlist = pickle.load(f) # 从文件载入对象
print(storedlist)

输出:

$ python3 pickling.py
['苹果', '芒果', '胡萝卜']

它是如何工作的:

要在文件中存储一个对象,我们首先必须以'w'rite写'b'inary 二进制格式的方式open打开文件,然后调用pickle模块的dump函数,这个过程叫拾取

接下来,我们使用pickle模块的load函数取回对象,这个过程叫做拆开

小结

我们已经讨论了各种类型的输入/输出,文件处理和使用pickle模块。

接下来,我们将探讨索异常的概念。

异常处理

在你的程序中出现某些异常情况时,异常发生。例如,如果你要读一个文件而文件不存在?或者如果你不小心删除了正在运行的程序?这种情况的处理使用异常处理。

同样,如果您的程序有一些无效的语句会怎样呢?这是由Python以举起它的句柄,告诉你有一个错误的方式处理。

错误

考虑一个简单的print函数调用。如果我们把print错拼为Print,注意大小 写。在这种情况下,Python会提出一个语法错误。

>>> Print('世界你好')
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    Print('世界你好')
NameError: name 'Print' is not defined
>>> print('世界你好')
世界你好

观察到出现了一个NameError,且错误发生的地方也被打印出来,这就是为这个错误,错误处理程序所做的。

异常

我们将尝试读取来自用户的输入。按ctrl-d后看看会发生什么?

>>> s = input('输入一些东西 --> ')
输入一些东西 --> 
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    s = input('输入一些东西 --> ')
EOFError: EOF when reading a line

Python会产生一个称为EOFError的错误,这基本上意味着它发现了一个不希望看到的一个文件结束符号 (这是由ctrl-d表示)。

处理异常

我们可以使用try..except语句处理异常,我们基本上把通常的语句放在try块,把所有的错误处理程序放在except块。

例子 (保存为 try_except.py):

try:
    text = input('输入一些东西 --> ')
except EOFError:
    print('为什么你要输入文件结束符呢?')
except KeyboardInterrupt:
    print('你取消了操作。')
else:
    print('你输入了 {0}'.format(text))

Output:

$ python3 try_except.py
Enter something -->     # 按ctrl-d
为什么你要输入文件结束符呢?

$ python3 try_except.py
Enter something -->     # 按ctrl-c
你取消了操作。

$ python3 try_except.py
Enter something --> 没有例外
你输入了 没有例外

它是如何工作的:

我们把所有的可能导致异常/错误的语句放在try块中,然后把适当的错误/异常处理程序放在except子句/块中。except子句可以处理一个指定的错误或异常,或一个圆括号括起来的错误/异常列表。如果没有提供错误或异常的名字,它将处理所有错误和异常。

注意,每一个try子句,必须有至少一个except子句与之相对应,否则,要一个试try块有什么用?

如果不处理任何错误或异常,那么默认的Python处理程序被调用,它只是停止程序的执行和输出错误消息。这点在上面我们已经看到了。

try..except块相关,也可以使用else子句,没有例外发生的时候执行else子句。

在下面的例子中,我们还将看到如何获得异常对象,以便我们重新得到附加的信息。

提出异常

你可以使用raise语句通过指定错误/异常的名字提出异常,异常对象也被抛出

你可以提出的错误或异常应该是一个类,它必须直接或间接地是一 Exception (异常)类的一个派生类。

例子 (保存为 raising.py):

class ShortInputException(Exception):
    '''一个用户定义的异常类。'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast

try:
    text = input('输入一些东西 --> ')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
    # 像通常一样可以继续其它的工作
except EOFError:
    print('为什么输入文件结束符?')
except ShortInputException as ex:
    print('输入短的例外: 输入有 {0} 长, 预期至少 {1}'\
          .format(ex.length, ex.atleast))
else:
    print('没有异常提出。')

Output:

$ python3 raising.py
输入一些东西 --> 中国
输入短的例外: 输入有 2 长, 预期至少 3

$ python3 raising.py
输入一些东西 --> 中国功夫
没有异常提出。

它是如何工作的:

这里,我们创建了我们自己的异常类型,叫做ShortInputException,它有两个字段-输入字符串的长度 length 和程序期望的最小长度atleast

except 子句,我们提到被保存变量名字的错误的类,控制相应的错误/异常对象,类似于在函数调用的的实参和形参。 在这个特别的except子句中,我们使用异常对象的lengthatleast字段,给用户打印一个适当的消息。

Try .. Finally

假设在你的程序中正在阅读一个文件,你如何确保文件对象是正常关闭,是否产生了常?这可以通过使用finally块实现。注意,,在相同的相应的try块中,您可以使用一个except子句以及 finally块。如果你想同时使用,你将不得不将一个嵌入到另一个中。

保存为finally.py:

import time

try:
    f = open('poem.txt')
    while True: # 通常的读文件
        line = f.readline()
        if len(line) == 0:
            break
        print(line, end='')
        time.sleep(2) # 确保它运行了一会儿
except KeyboardInterrupt:
    print('!! 你取消了从文件读取操作。')
finally:
    f.close()
    print('(清理:关闭文件)')

Output:

$ python3 finally.py
当工作完成时
编程是有趣的
如果想让你的工作有趣
    使用Python!
(清理:关闭文件)

它是如何工作的:

我们做了通常的文件读取工作,但是为了程序缓慢地运行(Python在自然状况下运行非常快)我们武断地使用time.sleep函数在每打印完一行后延迟2秒。当程序仍在运行时,,按ctrl-c可以中断/取消该程序的运行。

观察到 KeyboardInterrupt异常发生程序退出。然而,在程序退出前,最后子句被执行,文件对象总是被关闭。

with语句

try块中获取资源和随后的在finally块释放释放资源是一种常见的模式。因此,这还有一个with语句,它使这一切以一个干净的方式运行:

保存为 using_with.py:

with open("poem.txt") as f:
    for line in f:
        print(line, end='')

它是如何工作的:

输出应该与前面的示例相同,不同的是,我们使用的是带有with语句的open函数 --我们我们把关闭文件的工作留给with open自动完成。

在幕后所发生的是,使用with语句有一个协议。它通过open语句获取返回的对象,在这种情况下我们称之为"thefile"。

在开始代码块时,它通常称为 thefile.__enter__,代码块执行完毕时,通常称为thefile.__exit__

所以,在finally块中我们要写的代码将被 __exit__方法自动小心,这有利避免重复显式使用 try..finally

这一主题的更多讨论已超本书范围,更多的解释请参考PEP 343

小结

我们已经讨论了try..excepttry..finally语句的使用方法。我们已经看到了如何创建我们自己的异常类型和如何提出异常。

接下来,我们将探索Python标准库。

标准库

Python标准库包含大量有用的模块,是每个标准Python安装的一部分。如何你熟悉这些库能做的事的范围,许多问题可以快速解决,因此,熟悉Python标准库是很重要的,

在这个库中,我们将探讨一些经常使用的模块。在你安装的Python的文档中'Library Reference' section,你可以找到在Python标准库中所有模块的完整细节。

让我们探讨几个有用的模块:

注意
如果你发现本章的主题太先进,你可以跳过这一章。但是,当你更喜欢使用Python编程时,我强烈建议你回到本章。

sys(系统)模块

sys模块包含系统特定的功能。我们已经看到, sys.argv列表包含的命令行参数。

假设我们想要检查正在使用的Python命令的版本,也就是说,我们希望确保我们使用至少是版本3。sys模块给了我们这样的功能。

$ python3
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=3, micro=0, releaselevel='final', serial=0)
>>> sys.version_info.major >= 3
True

它是如何工作的:

sys模块有一个给我们版本信息的version_info元组,第一部分是 major主版本号,例如,我们可以检查确保程序在Pythpn 3.0版本下运行。

保存为versioncheck.py:

import sys, warnings
if sys.version_info.major < 3:
    warnings.warn("程序需要在 Python 3.0 以上版本上运行",
        RuntimeWarning)
else:
    print('按正常情况处理')

Output:

$ python2.7 versioncheck.py
versioncheck.py:6: 程序需要在 Python 3.0 以上版本上运行
  RuntimeWarning)

$ python3 versioncheck.py
按正常情况处理

它是如何工作的:

我们使用标准库的另一个模块,称为warnings(警告),用于向最终用户显示警告。如果Python版本号小于3,我们显示相应的警告。

logging(日志)模块

如果你想把一些调试信息或重要信息存储在某个地方,以便你可以检查你的程序是否按你的期望运行,你该如何做呢?你怎样将这些信息"存储在某个地方"?这可以通过使用 logging模块实现。

保存为 use_logging.py:

import os, platform, logging

if platform.platform().startswith('Windows'):
    logging_file = os.path.join(os.getenv('HOMEDRIVE'), os.getenv('HOMEPATH'), 'test.log')
else:
    logging_file = os.path.join(os.getenv('HOME'), 'test.log')

print("记录到", logging_file)

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s : %(levelname)s : %(message)s',
    filename = logging_file,
    filemode = 'w',
)

logging.debug("程序开始")
logging.info("做些事情")
logging.warning("现在结束")

Output:

$ python3 use_logging.py
记录到 C:\Users\swaroop\test.log

如果我们检查test.log文件的内容,它看起来像:

2013-02-05 21:38:24,592 : DEBUG : 程序开始
2013-02-05 21:38:24,592 : INFO : 做些事情
2013-02-05 21:38:24,592 : WARNING : 现在结束

它是如何工作的:

我们从标准库使用三个模块,os(操作系统)模块,与操作系统交互,platform(平台)模块,操作平台的信息,信息平台即操作系统和logging(日志)模块,记录信息。

首先,我们根据platform.platform() (更多信息,见import platform; help(platform))返回的字符串检查我们正在使用哪个操作系统,如果是Windows,我们找出我们要存储信息的主盘(磁盘驱动器),目录和文件名。把这三部分组装在一起,我们得到文件的完整位置。其他平台,我们需要知道用户的主文件夹,然后我们就得到了完整的文件的位置。

我们使用os.path.join()函数把这3部分连接在一起。之所以使用一个特殊的函数而不仅仅是字符串连接,是因为这个函数将确保整的位置的格式与操作系统所期望的相匹配。

我们配置logging模块使之以我们指定的特定格式来编写所有的信息。

最后,我们旋转为了调试、信息、警告甚至至关重要的信息。一旦程序已经运行,我们可以检查这个文件,我们将会知道在程序中发生了什么事,即使没有信息显示给运行程序的用户。

Week(周)系列模块

在标准库中,有很多需要探索如 debugging(调试),handling command line options(处理命令行选项),regular expressions(正则表达式)等等。

进一步探索标准库的最好方法就是阅读 Doug Hellmann的优秀的Python Module of the Week系列或阅读[Python文档](http://docs.python.org/py3k/)。

小结

我们已经探索过了Python标准库中许多模块的一些功能。强烈建议浏览[Python Standard Library documentation(Python标准库文档)](http://docs.python.org/py3k/library/index.html)来了解可用的所有模块。

接下来,我们将讨论Python的各个方面,这将使我们的Python旅程更完整

更多

到目前为止,我们已经涵盖了您将使用的Python各个方面的大部分。在这一章,我们将介绍使我们的Python知识更全面的更多方面。

Passing tuples around

你是否一直希望可以从一个函数返回两个不同的值?你可以,你所要做的就是使用一个元组。

>>> def get_error_details():
...     return (2, '第2个错误的细节')
...
>>> errnum, errstr = get_error_details()
>>> errnum
2
>>> errstr
'第2个错误的细节'

注意,a, b = <some expression>的用法是表示将表达式的结果作为一个有两个值的元组。

如果你想将结果解释为(a, <其它的一切>),那么你只需要以星号开始,就像你在函数参数中做的:

>>> a, *b = [1, 2, 3, 4]
>>> a
1
>>> b
[2, 3, 4]

这也意味着,在Python中以最快的方式交换两个变量是:

>>> a = 5; b = 8
>>> a, b = b, a
>>> a, b
(8, 5)

特别的方法

有一些特别的方法,比如__init____del__方法在类中有特殊意义。

特殊的方法用于模拟内置类型的某些行为,例如,如果您想要使用x[key]索引操作你的类(就像你用在列表和元组中使用的),那么你所要做的就是实现__getitem__()的方法,做你的工作。如果你仔细想想,这就是Python为list类本身所做的!

在下面的表中列出了一些有用的特别的方法。如果你想知道所有的特殊方法,参见手册

__init__(self, ...)

这个方法在新创建对象返回用法之前被调用。

__del__(self)

在对象被销毁前调用

__str__(self)

当使用print函数或str()函数时调用。

__lt__(self, other)

当使用小于(<)操作符时调用,同样的,所有的操作符(+, >,等。)都有特别的方法。

__getitem__(self, key)

当使用x[key]索引操作时调用。

__len__(self)

当序列对象的内建len()函数使用时调用。

单语句块

我们已经看到,每个块语句以它自己的缩进级别与其它部分分离。嗯,有一个警告。如果你的块语句只包含一个单独的语句,那么您可以指定在同一行,例如,一个条件语句和循环语句。下面的示例应该清楚地说明这一点:

>>> flag = True
>>> if flag: print('是的')
是的

请注意,单个语句用于原地而不是作为一个单独的块。虽然,你这可以让你的程序较小,我强烈建议避免这种简化方法,除非是错误检查,主要因为如果你正在使用适当的缩进,它会更容易添加一个额外的声明。

Lambda 形式

一个lambda语句是用来创建新的函数对象。从本质上讲,lambda取得一个参数,后面有一个函数主体的表达式,而表达式表达式的值由新函数返回。

例子 (保存为 lambda.py):

points = [ { 'x' : 2, 'y' : 3 }, { 'x' : 4, 'y' : 1 } ]
points.sort(key=lambda i : i['y'])
print(points)

输出:

[{'x': 4, 'y': 1}, {'x': 2, 'y': 3}]

它是如何工作的:

注意,listsort方法可以取一个key(键)参数,用于确定列表如何排序(通常我们知道只有升序或降序)。在我们的例子中,我们要做一个自定义排序,为此,我们需要编写一个函数,我们只使用一个lambda表达式创建一个新的函数,而不是为只在这一个地方使用的函数写一个单独的def块。

列表解析

列表解析用于从现有的列表派生一个新列表。假设您有一个数字列表,你想要得到一个相应的列表:只有当数字本身大于2时所有的数字乘以2,列表解析非常适合这种情况。

例子 (保存为list_comprehension.py):

listone = [2, 3, 4]
listtwo = [2*i for i in listone if i > 2]
print(listtwo)

输出:

$ python3 list_comprehension.py
[6, 8]

它是如何工作的:

这里,我们在满足一些条件的情况下(if i > 2)通过指定的操作(2*i)得到一个新列表。注意,原始列表仍未修改。

使用列表解析的优势是:当我们使用循环来处理列表中的每个元素,并将其存储在一个新的列表时,它可以减少需要代码的引用数量。

在函数中接受元组和字典

函数有一种特殊的方式接收参数--分别使用*前缀标识元组或字典。当函数获取数量可变的参数时这很有用。

>>> def powersum(power, *args):
...     '''返回每个参数指定次方的和。'''
...     total = 0
...     for i in args:
...         total += pow(i, power)
...     return total
...
>>> powersum(2, 3, 4)
25

>>> powersum(2, 10)
100

因为我们有一个*前缀在args变量前,所有额外的传递给函数的参数存储作为元组的args 中。如果使用**前缀,额外的参数将被认为是键/值对的字典。

assert(断言)语句

assert语句是用来声称某样东西是确实的。例如,如果你很确定你将使用的列表中至少有一个元素,而你想检查如果它不是真实的出现一个错误。那么assert语句在这种情况下是一个好主意。当断言语句失败,出现一个AssertionError(断言错误)。

>>> mylist = ['条款']
>>> assert len(mylist) >= 1
>>> mylist.pop()
'条款'
>>> mylist
[]
>>> assert len(mylist) >= 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError

assert语句应该明智而审慎地使用。大多数时候,它能很好地捕捉异常,要么处理问题或向用户显示一条错误消息,然后退出。

转义字符

假设,你想有一个包含单引号(')的字符串,你将如何指定该字符串呢?例如,字符串是What's your name?,你不能指定为'What's your name?'因为Python会对在哪里开始和结束字符串感到困惑。所以,你必须指定这个单引号不表示是字符串的末尾。这可以在叫做转义字符的帮助下实现。你将单引号指定为\'——注意,反斜杠。现在,您可以指定字符串为'What\'s your name?'

指定这个特定的字符串的另一种方式是"What's your name?",即使用双引号。同样的,在一个双引号的字符串中为双引号本身必须使用一个转义字符。另外,对反斜杠本身要使用转义字符 \\

如果你想指定一个两行的字符串该怎样在做呢?一种方法是使用[前面]](#三重引号) 介绍的三重引号,或者您可以使用新行转义字符--\n来表示一个新行开始。一个例子是 这是第1行\n这是第2行。另一个要知道的有用的转义字符是制表符 \t。有更多的转义字符,但在这我只提到最有用的几个。

需要注意的是,在一个字符串中,在一行结束处的反斜杠表明字符串在下一行仍在继续,并没有换行。例如:

"这是第1个句子。\
这是第2个句子。"

相当于

"这是第1个句子。这是第2个句子。"

原始字符串

如果你需要指定没有经过像转义字符一样进行处理的字符串,那么,你需要通过对字符串前加前缀rR来指定一个原始的字符串。一个例子是 r"\n表明新行"

正则表达式用户需要注意
在处理正则表达式时总是使用原始字符串,否则,需要大量的反斜杠。例如,反向引用可以被指向'\\1'r'\1'

小结

在这一章,我们讨论了一些Python的更多功能,然而我们还没有覆盖所有Python的特点。然而,,在这个阶段,我们已经覆盖了在实践中你会用到的大部分。对于你开始创造任何程序是足够的。

接下来,我们将讨论如何深入探索Python。 # 下一步做什么

到现在为止,如果你彻底读过这本书和练习写作过大量的程序,那么你一定变得舒服而且熟悉Python。你可能已经创建了一些Python程序去尝试这些东西,去锻炼你的Python技能。如果你还没有这样做了,你应该去做。现在的问题是“下一步做什么呢?”。

我建议你处理这个问题:

创建你自己的命令行地址簿程序,使用它可以浏览、添加、修改、删除和搜索你的联系人如朋友、家人和同事及他们的信息,如电子邮件地址和/或电话号码。细节必须存储供以后检索。

如果你想依据直到现在我们遇到的各种各样的材料中考虑它,这是相当容易的。如果你仍然想知道如何处理的方向,那么这是一个提示。

提示(不先试不要看)
创建一个代表人们信息的类。使用字典存储成员对象,用他们的名字做键。使用pickle模块将对象存储在你的硬盘中,使用字典的内建方法添加、删除和修改成员信息。

一旦你能做这个,你可以声明是一名Python程序员了。现在,立即发电子邮件给我感谢我的这本伟大的书;-). 这个步骤是可选 的但是推荐的。同样,请考虑购买一个印刷版来支持这本书的可持续发展。

如果你发现这个程序简单,这有另外一个:

编写替换命令. 这个命令将使用指定文件列表中的字符串替换一个字符串。

replace命令可以是简单或复杂的,这随你所愿,从简单的字符串替换到寻找模式(正则表达式)。

在那之后,下面是一些继续使用Python旅程的一些方法:

示例代码

学习一门程序语言的最好方式是写许多代码和读许多代码:

问题及解答

学习指南

视频

讨论

当你遇到一个Python问题而无法进行下去时,不知道去问谁,那么 python-tutor list(Python辅导列表) 是你提问的最好的地方。

前提是确保你做你的家庭作业和你自己试图解决问题。

新闻

如果你想了解世界是最新的Python,那么请跟随[Python的官方行星](http://planet.python.org。

安装库

在[Python包索引)(http://pypi.python.org/pypi)中有一个巨大的数量的,你可以在你自己的程序中使用的开源库。安装和使用这些库,你可以使用pip

图形软件

假设您想要使用Python创建自己的图形程序,这可以通过使用一个GUI(图形用户接口)库与Python绑定来实现。绑定是允许你用Python编写程序和使用用C或C++语言编写的库。

使用Python有很多GUI可供选择:

Kivy

http://kivy.org

PyGTK

这是Python为GTK+工具包的绑定,GTK+工具包是GNOME建立的基础。GTK+在使用上有许多怪癖,但一旦你适应,您可以快速创建GUI应用程序。这是图形界面设计者必不可少的。文档是没有改善。GTK+在Linux上工作良好,但它转到Windows是不完整的。您可以使用GTK+创建自由以及专有软件。 要开始使用,阅读[PyGTK教程)(http://www.pygtk.org/tutorial.html)。

PyQt

这是Python为Qt工具包绑定的,Qt工具包是KDE建立的基础。由于Qt设计器和惊人的Qt文档,Qt非常易用、非常强大。如果你想创建开源(GPL'ed)软件,PyQt是免费的。如果你想创建专有的闭源软件,你需要购买它。从Qt4.5开始你同样可以使用它创建非gpl软件。要开始使用,首先阅读PyQt教程或(PyQt书)(http://www.qtrac.eu/pyqtbook.html)。

wxPython

这是Python为wxWidgets工具包绑定的。wxPython有一个与之关联的学习曲线。然而,它是非常轻便,运行在Linux、Windows、Mac和甚至嵌入式平台。有许多可供wxPython使用的ide,包括GUI设计师,例如SPE(Stani的Python编辑器)wxGladeGUI构建器。使用wxPython您可以创建自由以及专有软件。首先,阅读[wxPython教程)(http://zetcode.com/wxpython/)。

GUI工具小结

更多选择,请参见[Python官方网站的GuiProgramming wiki页面)(http://www.python.org/cgi-bin/moinmoin/GuiProgramming)。

不幸的是,对于Python没有一个标准的GUI工具。我建议你根据你的情况,从以上工具中选择一个。第一个因素是,你是否愿意为使用GUI工具支付费用。第二个因素是,你是否想让程序只在Windows、或Mac或Linux上运行,还是所有这些都可以。第三个因素,如果Linux是选择的平台,你是Linux上的KDE用户,还是GNOME用户。

对于更详细和全面的分析,请看The Python Paper,Volume 3, Issue 1的第26页。

各种实现

一个编程语言通常有两部分,语言和软件。语言是你怎样写点东西。软件是程序实际运行什么

我们一直使用CPython软件来运行我们程序。因为它写是用C语言编写的,Classical(经典)的Python解释器,因此被称为CPython。还有其它的软件可以运行你的程序:

Jython

运行在Java平台上的一个Python实现。这意味着您可以在Python语言中使用Java库和类,反之亦然。

IronPython

运行在.NET平台的一个Python实现,这意味着您可以在Python中使用.NET库和类,反之亦然。

PyPy

用Python写的一个Python实现!这是一个研究项目,使改进解释器又快又好,因为解释器本身是用一种动态语言编写的(与静态语言相反(如C、Java或c#)在上面的三个实现)

Stackless Python

: 一个Python实现,是专门基于线程性能的。

还有其它的,例如,CLPython - 用 Common Lisp语言编写的一个Python实现、[IronMonkey],这是一个工作在JavaScript解释器上的IronPython端口,这可能意味着您可以使用Python(而不是JavaScript)来写你的网络浏览器("Ajax")程序。

每一种实现都有自己的有用的专业领域。

函数程序设计(为高级读者)

当你开始编写大型程序,你应该明确地了解更多我们在(面向对象编程一章)(#面向对象编程)学会的,关于函数编程方法与基于类的编程方法。

小结

现在,我们到了本书尽头,但是,正如他们所说,这是结束的开始!。你现在是一个狂热的Python用户,毫无疑问,你准备使用Python解决许多问题。你可以让你的计算机自动开始做各种各样的,从前不可想象的事情或编写自己的游戏和许许多多。所以,开始吧!

免费/自由和开放源码软件

Free/Libre and Open Source Software(免费/自由和开放源码软件),缩写为FLOSS,是基于社区的概念,它本身是基于共享的概念,尤其是知识的共享。FLOSS是免费使用、修改和再分配的。

如果你已经读过这本书,那么你已经熟悉FLOSS,因为你已经一直使用Python,Python是一个开源的软件!

这里是社区共享的和可以创建的各种各样事情给出一种想法的FLOSS的一些例子:

Linux

这是一个使用在GNU / Linux操作系统中的一个FLOSS操作系统内核。Linux,内核,是由Linus Torvalds还是学生的时候开始的。安卓系统是基于Linux。现在你使用的任何网站主要是在Linux上运行。

Ubuntu

这是一个社区驱动的发行版,由Canonical赞助,这是今天最流行的Linux发行版。它允许您安装大量的可用FLOSS,所有这些都有易于使用和易于安装的方式。最好的是,你可以重启你的计算机,在CD上运行GNU/Linux了!这允许在你的电脑安装操作系统前完全尝试新的操作系统。然而,Ubuntu是不完全自由软件;它包含专有的驱动、固件和应用程序。

LibreOffice

: 这是一个很好的社区主导型和成熟的办公套件,包括一个作家、演示、电子表格和其中的画图组件。它甚至可以轻松地打开并编辑微软的Word和PowerPoint文件。它在几乎所有平台上运行,且是完全免费的,自由和开放源码软件。

Mozilla Firefox

这是最好的极其快速的网络浏览器,其明智和深刻的特性已经获得了评论界的好评。扩展的概念允许使用任何类型的插件。

它包含Thunderbird是一个优秀的电子邮件客户端,它使邮件阅读轻而易举。

Mono

这是微软.NET平台上开放源码的一个实现。它允许在GNU/Linux, Windows, FreeBSD, Mac OS和其它许多平台上创建和运行.NET应用。

Apache web server

这是流行的开源web服务器。事实上,它是地球上最流行的web服务器!近一半以上的网站在运行它。是的,是这样--Apache处理比所有竞争(包括Microsoft IIS)的组合更多的网站。

VLC Player

这是一个视频播放器,可以播放从DivX到MP3、Ogg、vcd、dvd到……谁说开源不好玩呢?:-)

这个列表仅仅是给你一个简短的想法--有很多优秀的FLOSS,比如Perl语言,PHP语言,Drupal网站内容管理系统,PostgreSQL数据库服务器,TORCS赛车游戏,KDevelop IDE,Xine——电影播放器,VIM编辑器,编辑器,Quanta+音频播放器,GIMP图像编辑程序,…这个列表可以永远持续下去。

得到FLOSS世界最新的声音,看看下面的网站:

关于更多FLOSS的信息,请访问下列网站:

所以,继续探索广阔、自由和开放的FLOSS世界! # 跋

本书用到的所有软件几乎都是FLOSS

本书的诞生

我用Linux的Red Hat 9.0作为组织本书初稿的基础,我使用Linux的Fedora Core 3作为组织本书第6草稿的基础。

最初,我是使用KWord写这本书(在前言的[历史课程]中(#历史课程)中解释的)。

初长成

后来,我使用Kate转到了DocBook XML,但我发现太乏味。所以,我切换到带有级别控制的OpenOffice,它提供与生成PDF一样好的格式,但它从文档生成HTML非常浅陋。

最后,我发现XEmacs,当我决定把DocBook XML格式作为长远的解决方案后,我(再一次)重写了本书。

在第六个草稿,我决定使用Quanta+做所有的编辑。来自Linux的Fedora Core 3 的标准XSL样式表正在被使用。然而,我为HTML页面的颜色和样式写了一个CSS文件。我也写了一个粗糙的词法分析程序,当然,Python为所有列出的程序自动提供语法高亮显示。

对于这个第七草稿中,我使用[MediaWiki)(http://www.mediawiki.org)为我组织。现在,我在线编辑的任何东西,读者都可以在wiki网站直接读/编辑/讨论。

我使用Vim编辑,感谢用 Vin整合的[为Firefox的扩展ViewSourceWith)(https://addons.mozilla.org/en-US/firefox/addon/394)。

现在

使用 Vim, Pandoc, 和 Mac OS X.

关于作者

http://www.swaroopch.com/about/

修订历史

翻译

感谢许多孜孜不卷的志愿者,本书有许多使用不同人类语言的翻译。

如果你想翻译,请看下面的语言和志愿者列表,决定是想开始一个新的翻译或是帮助已有的翻译项目。

如果你想开始一个全新的翻译,请看翻译的基本知识

阿拉伯语

下面是阿拉伯语版本的链接。感谢Ashraf Ali Khalaf翻译本书,(在这里)(http://www.khaledhosny.org/byte-of-python/index.html)你可以在线阅读整本书,或从sourceforge.net下载它,更多的信息点击这里

巴西葡萄牙语

有两种翻译:

当Python在2.3.5版本时,Samuel Dias Neto (samuel.arataca-at-gmail-dot-com)做了本书巴西葡萄牙语的第一版。

Samuel的翻译在aprendendopython.

Rodrigo Amaral (rodrigoamaral-at-gmail-dot-com)志愿把本书翻译为巴西葡萄牙语。

Rodrigo的翻译在http://www.swaroopch.org/notes/Python_pt-br:Indice.

加泰罗尼亚语

Moises Gomez (moisesgomezgiron-at-gmail-dot-com)志愿把本书翻译为加泰罗尼亚语。翻译正在进行中,在erstwhile wiki可以看到。

Mois猫s G贸mez - 我是一名开发者,也是一名编程教师(通常教没有任何经验的人们).

前一段时间,我需要学习如何用Python编程,Swaroop的工作真有帮助,足够清楚、简洁、完整,这正是我需要的。

在这次的经验,我想让我们国家的一些人也从中受益,但是英语可能是一个障碍。

所以,为什么不试着翻译它呢?我做了Bop的以前版本。

我、我的国家有两种语言。假设别人会将其转换为更广泛的西班牙语,我选择了加泰罗尼亚语言。

中文

当前版本:

历史版本:

繁体中文

Fred Lin (gasolin-at-gmail-dot-com)志愿把本书翻译为繁体中文。

http://code.google.com/p/zhpy/wiki/ByteOfZhpy可以看到。

这个翻译的一个令人兴奋的特点是,它也含有与原始Python代码并列的可执行中文Python源代码。 > Fred Lin - 我作为一名网络固件工程师在三角洲网络工作,我也是TurboGears网络框架的一名贡献者。 > > 作为一名python的传教士(:- p),我需要一些材料来促进python语言。我发现的'A Byte of Python'对于新手和有经验的程序员击中了要点。'A Byte of Python'以可接受的篇幅阐述了Python本质。 > > 翻译最初基于简体中文版本,很快做了大量的重写以适合当前版本和阅读重量。 > > 最近的繁体中文版本同样具有可执行的中文python源代码,这是通过我的新'zhpy'(python中文版)项目实现的(从07年8月开始)。 > > zhpy(发(Z.H.?,或zippy音)在Python上建立一个层,翻译或用中文(繁体或简体)与Python交互(Traditional or Simplified)。这个项目的主要目的是为了教学。

法语

Gregory (coulix-at-ozforces-dot-com-dot-au)志愿把本书翻译为法语。

G茅rard Labadie (Palmipede)完成了本书的法语翻译,它从http://www.swaroopch.org/notes/Python_fr:Table_des_Mati%C3%A8res开始。

德语

Lutz Horn (lutz-dot-horn-at-gmx-dot-de), Bernd Hengelein (bernd-dot-hengelein-at-gmail-dot-com) 和 Christoph Zwerschke (cito-at-online-dot-de) 志愿把本书翻译为德语。

他们的翻译在 http://abop-german.berlios.de

Lutz Horn 说:

我32岁,有一个德国海德堡大学数学学位。目前,我作为一名软件工程师在公共资金资助的,以建立一个所有与计算数学有关的门户网站的项目上工作。作为职业我使用的主要语言是Java,但我试着尽可能在幕后使用Python,使用Python尤其是用来作文本分析和转换是非常容易的。我不是很熟悉GUI工具包,因为大多数的编程是关于web应用程序,用户界面使用Java框架像Struts等构建。目前,我想制造更多的Python和它的生成器的函数功能的用途。经过短期观察Ruby,这个语言的块的使用给我留下非常深刻的印象。通常我喜欢语言的动态特性,像Python和Ruby,因为它允许我做在更多的静态语言像Java不能做的事。我已经寻找了很多适合教一个完全非程序员的编程。我发现这本书 'How to Think Like a Computer Scientist: Learning with Python(如何像计算机科学家一样思考:学习与Python)'和'Dive into Python(深入Python)',第一个是适合初学者但翻译很长,第二个不适合初学者。我认为 'A Byte of Python'很好地介于两者之间,它不是很长,切入要点,同时详细到足以教一个新手。除此之外,我喜欢简单的DocBook结构,这使得翻译文本生成以各种格式的输出成为一个魅力。

Bernd Hengelein 说:

Lutz和我要在一起做德语的翻译。我们刚刚开始简介和前言,但是我们会让你了解我们所取得的进步。好的,下面是我的一些个人资料。我34岁,自1980年开始玩电脑,当时"海军准将C64(注:一款计算机)"统治了托儿所。在研究计算机科学后,我开始成为一名软件工程师。目前我从事医学成像领域,在德国一家大型公司工作。尽管C++是我日常工作的(不得不)使用的主要语言,我还是不断寻找新东西要学。去年我爱上了Python,这是一个美妙的语言,无论其发展潜力还是它的美。我在网上某处读到过一个家伙说他喜欢python,因为代码看起来如此美丽。在我看来他说的非常正确。当时我决定学习python,我发现可用的好的德语文档很少。当我遇到你的书,一个德语翻译的想法突然闪过我的脑海。幸运的是,鲁茨有同样的想法,我们现在可以分工。我期待一个好的合作!

希腊语

The Greek Ubuntu Community translated the book in Greek, for use in our on-line asynchronous Python lessons that take place in our forums. Contact @savvasradevic for more information.

Greek Ubuntu Community(希腊乌班图社区)(用希腊语翻译了本书)(http://wiki.ubuntu-gr.org/byte-of-python-el),在我们的论坛有我们异步Python教程。更多信息,联系(@savvasradevic](https://twitter.com/savvasradevic)。

印尼语

http://python.or.id/moin.cgi/ByteofPythonDaniel(daniel-dot-mirror-at-gmail-dot-com)正在把本书翻译为印尼语。

W. Priyambodo也志愿把本书翻译为印尼语,翻译正在进展,它在http://www.swaroopch.org/notes/Python_id:Daftar_Isi.

意大利语

Enrico Morelli (mr-dot-mlucci-at-gmail-dot-com) 和 Massimo Lucci (morelli-at-cerm-dot-unifi-dot-it) 志愿把本书翻译为意大利语。

意大利语翻译在www.gentoo.it/Programmazione/byteofpython。新在翻译正在进行中,从http://www.swaroopch.org/notes/Python_it:Prefazione开始。

Massimo Lucci 和 Enrico Morelli -我们正在进行(意大利)佛罗伦萨大学的化学系工作。我(Massimo)是一名核磁共振光谱仪服务工程师和系统管理员,Enrico是我们CED和并行/集群系统的一名服务工程师和系统管理员。我们有7年python编程经历,我们有10年Linux平台工作经验。在意大利,我们负责管理Gentoo Linux发行的www.gentoo.it,www.nmr.it(在建)为核磁共振应用和国会组织和管理。就这些!我们为用你的书而感到感动,我们认为对于接近Python的新用户是必要的(我们正在考虑几百学生和研究人员致力于我们的实验室)。

日语

日语的一个版本在http://www.swaroopch.org/notes/Python_ja:Table_of_Contents

Shunro Dozono (dozono-at-gmail-dot-com)正在把本书翻译为日语。

蒙古语

Ariunsanaa Tunjin (luftballons2010-at-gmail-dot-com)志愿把本书翻译为蒙古语。

2009年11月的更新 : Ariunsanaa的翻译接近完成。

挪威语(bokm氓l)

Eirik V氓geskar (http://www.swaroopch.org/notes/User:Vages)是在挪威Sandvika videreg氓ende skole的高中生。[博客]在(http://forbedre.blogspot.com/),现在正在将本书翻译为挪威语(bokm氓l)。翻译工作正在进行,细节详见http://www.swaroopch.org/notes/Python_nb-no:Innholdsfortegnelse

Eirik V氓geskar: 我一直想要编程,但是因为我使用小语种,学习的过程是非常困难的。大多数教程和书籍都用非常专业的英语写成,所以大多数高中毕业生甚至都没有理解教程大概的词汇。当我发现这本书,我所有的问题就解决了。"A Byte of Python"使用能简单就简单的非技术语言去解释一个编程语言,这两件事使我学习Python产生了乐趣。当读了一半后,我认为这本书值得翻译。我希望翻译会帮助和我有相同处境(尤其是年轻人)的那些人,也许使用较少的技术知识可以在人们之间传播这种语言。

波兰语

Dominik Kozaczko (dkozaczko-at-gmail-dot-com)志愿把本书翻译为波兰语。翻译正在进行,它的主页在: Uk膮艣 Pythona.

更新 : 到2009年10月2日,翻译已经完成。感谢Dominik,他的两个学生和他们的朋友所付出的时间和精力!

Dominik Kozaczko - 我是一名计算机科学与信息技术老师。

葡萄牙语

Fidel Viegas (fidel-dot-viegas-at-gmail-dot-com)志愿把本书翻译为葡萄牙语。

罗马尼亚语

Paul-Sebastian Manole (brokenthorn-at-gmail-dot-com) 志愿把本书翻译为罗马尼亚语。

Paul-Sebastian Manole - I'm a second year Computer Science student at Spiru Haret University, here in Romania. I'm more of a self-taught programmer and decided to learn a new language, Python. The web told me there was no better way to do so but read ''A Byte of Python''. That's how popular this book is (congratulations to the author for writing such an easy to read book). I started liking Python so I decided to help translate the latest version of Swaroop's book in Romanian. Although I could be the one with the first initiative, I'm just one volunteer so if you can help, please join me. 我是罗马尼亚Spiru Haret大学计算机科学二年级学生。在更大程度上,我是自学的程序员,决定学一门新的语言--Python。网络告诉我,除了读''A Byte of Python'' 没有更好的办法。本书是多么的流行(祝贺作者编写这样一个易读的书),我开始喜欢Python,所以我决定帮助用罗马尼亚语翻译Swaroop的最新版本的书在。虽然我可以成为第一次主动参与者之一,但我只是一个志愿者,所以如果你可以帮忙,请加入我。 翻译将在http://www.swaroopch.org/notes/Python_ro

俄语和乌克兰语

Averkiev Andrey (averkiyev-at-ukr-dot-net) 志愿把本书翻译为俄语,也许会翻译为乌克兰语(时间允许)。

Vladimir Smolyar (v_2e-at-ukr-dot-net)开始了一个俄语翻译维基页。你可以在http://www.swaroopch.org/notes/Python_ru:Table_of_Contents看开发版。

斯洛伐克语

Albertio Ward (albertioward-at-gmail-dot-com) 在fatcow.com/edu/python-swaroopch-sl/翻译了本书:

我们是一个非营利性的称为鈥淭教育鈥ranslation ?的组织,我们代表了斯拉夫大学的一群人,主要是学生和教授。这里的学生来自不同的院系:语言学、化学、生物学等。我们试图在互联网上找到有趣的和我们及我们的大学同事有关的出版物。有时我们自己发现文章,有时我们的教授帮助我们选择翻译材料。从作者处获得许可后,我们翻译文章,张贴在我们大学同事和朋友可以访问的博客中,这些翻译出版物经常在日常学习中帮助学生。

我为什么要选择你的文章进行翻译呢?它被用来帮助保加利亚人在所有可能的上理解本文。已经做过一些涉及新奇和相关性的话题,我发现对于那些生活在我的国家中的那些人,这是非常紧急的。所以,我认为它可以成为非常受欢迎的。语言障碍在这个小事中不再存在,因为在我的翻译被淘汰。 ## 西班牙语

Alfonso de la Guarda Reyes (alfonsodg-at-ictechperu-dot-net), Gustavo Echeverria (gustavo-dot-echeverria-at-gmail-dot-com), David Crespo Arroyo (davidcrespoarroyo-at-hotmail-dot-com) 和 Cristian Bermudez Serna (crisbermud-at-hotmail-dot-com) 志愿把本书翻译为西班牙语。翻译正在进行,你可以从http://www.swaroopch.org/notes/Python_es-ar:Tabla_de_Contenidos开始阅读西班牙语(阿根廷)的翻译。

Gustavo Echeverria 说:

我是工作在阿根廷的一名软件工程师。在工作中我主要使用c#和.Net技术,但是在我的个人项目中,严格使用Python或Ruby。很多年前我就知道Python,我被Python吸引了。我知道Python不久之后,我发现这本书,它帮助我学习Python。然后我志愿把本书翻译为西班牙语。现在,收到一些请求后,在Maximiliano Soler的帮助下,我开始翻译"A Byte of Python"。

Cristian Bermudez Serna 说:

我是(哥伦比亚)安蒂奥基亚大学通信工程的学生。几个月前,我开始学习Python,发现这个奇妙的书,所以我志愿把本书翻译为西班牙语。

瑞典语

Mikael Jacobsson (leochingkwake-at-gmail-dot-com)志愿把本书翻译为瑞典语。

土耳其语

T眉rker SEZER (tsezer-at-btturk-dot-net) 和 Bugra Cakir (bugracakir-at-gmail-dot-com) 志愿把本书翻译为土耳其语。土耳其语版本在哪?Bitse de okusak.

注意 : 在本页中提到的电子邮件地址中用@替换-at-,用.替换-dot-,用_替换-underscore-。邮件地址中其它地方的连字符仍然为-。 # 翻译的基本知识

Git的资源库有本书的所有原始资料。

forkthe repository(叉资源).

然后,提取资源到你的电脑,你需要知道怎样使用Git 来做它。

开始用你的本土语言编辑.pd文件。请阅读Pandoc README来了解文本的格式。

然后,按照README安装需要的能将原始文件转换为PDF等格式的软件。