这是一篇关于Python程序设计语言的入门教程,同时也是一份对编程艺术的简短介绍,其中的例子是用python写成的,希望读者们看完后会对python产生兴趣。
有必要注意一下的是,要使此文中的例子正确运行,你应该把它们写在一个文本文件中,然后用解释器运行。不要试图直接在交互方式下运行它们,不是所有的例子都可以这样运行。
1. 运行环境
要用python写程序,首先你必须先安装一个python的解释器。它可以存在于大多数平台(包括Macintosh、Unix和Windows)。还能够找到更多与此有关的信息在python的网站上,然后你还应该有一个文本编辑器(象emacs、notepad或者类似的东西)。
2. 编程是什么?
在计算机上编写程序其实就是给它一系列的指令告诉它去做什么。计算机程序在某些方面就象是产品的使用说明书,指导我们如何去安装产品。
当然,没有一台计算机会懂这个。那么,我们该如何做才能使计算机明白我们的指令呢?很重要的两点:首先,我们必须以计算机可以理解的方式与之交流,其次还要和它谈论它能够做到的事情。
第一点我们必须使用一种计算机才懂得的语言,一种已经为之准备好了解释器的程序设计语言。第二点我们可以让命令计算机做数字累加或者在屏幕上打印东西之类的事情。
3. Hello……
在程序设计教程有一个传统,那就是通常以在屏幕上打印“Hello, world!”这样的程序作为开始。对python来说,这非常简单:
这个指令告诉计算机要做什么:打印“Hello, world!”。如果让它执行更多任务该怎么做呢?很简单:
有时候我们希望计算机可以处理更多的元素,那么我们都有哪些元素呢?首先,有字符串,象“Hello, world!”,除此之外还有数字。假设我们打算让计算机为我们计算矩形的面积。我们可以给它如下的指令:
它是如何工作的呢?首先,以#开始的行叫做注释,而事实上会被计算机忽略。但是插入像这样小段的注释,对于增强你程序的可读性来说是很重要的。
接下来,看起来象 foo = bar 这样的行叫做赋值。对于 width = 20 这样的情况意味着告诉计算机,从这里开始width就代表20了。这意味着一个名字为“width”的变量从此被创建。这里注意一下,如果它先前已经存在,那么会被重新覆盖。当我们以后使用这个变量的时候,计算机就会知道它的值。因此,width * height本质上同20 * 30一样会计算出600这个结果,然后赋给名称为“area”的变量。程序的最后一句在屏幕上打印出变量“area”的值,所以你看到这个程序运行的最终结果仅仅是600。
在某些程序设计语言中,你必须在程序开始的时候告诉计算机你将会用到哪些变量。而python是一种聪明的语言,所以你可以根据需要随时创建。
4. 反馈
现在,随着不断的学习和了解,你可以执行一些简单的或者稍微复杂一点的计算了。比方说,你打算写一段程序来计算圆形的面积而不是矩形的:
然而,事实上计算圆形面积并不比计算矩形面积的那个程序更有意思。这个程序显得有些僵硬。如果我们看到半径为31的圆该怎么办?怎样让计算机知道?因此我们需要反馈,或者提示。计算机如何才能得知知道我们圆形的半径?同样需要输入资料,我们可以做的是告诉计算机圆的半径是多少:
现在程序变得好看多了,input是个被称为函数的东西,input是python内建的函数。仅仅写下input是不够的,你必须在它的后面放上一对括号。所以input()可以工作,它会简单的要求用户输入半径的长度。而上面的那个版本对用户来说也许更友好一些,因为它首先打印出了一个问题。当我们将诸如提问字符串“What is the radius?”之类的东西放在函数调用的括号中时,这个过程被称为函数的参数传递,而括号中的内容被称为参数。在上个例子中我们传递了一个提问作为参数以便input知道在获得答案前应该先打印什么。
但是获得的答案如何到达radius变量呢?函数input,调用时,会返回一个值,就像许多其它函数一样。你不一定非要使用这个值,但象我们这种情况,我们要使用它。这样,下面这两个表达式有着很大的差别:
foo现在包含input函数本身,所以它事实上可以像foo("What is your age?")这样使用,这被称为动态函数调用,而bar包含用户键入的值。
5. 流程
现在我们可以编写程序,让计算机执行简单的任务,例如运算和打印,并且可以获得用户输入了。这很有用,但仍然局限在按顺序执行命令,也就是意味着它们必须按照事先安排好的顺序执行。但是我们如何才能让计算机检查程序是否正确完成呢?
我们想做的,其实是可以通过控制程序的流程来实现。
注意:缩进在python中很重要。条件执行中的语句块必须被缩进,而且要缩进同等数量的空格(一个键相当于8个空格),以便解释器可以知道它们从哪里开始到哪里结束,同时也使程序变得更加可读。
让我们回到先前的面积计算问题。能看出来这段程序做什么吗?
从这个例子中可以看到新的概念是:
这个程序其实很简单,当用户打算让它计算矩形或是圆形的面积,它只需要一个数字。然后,使用一个if语句(条件执行)来决定应当执行哪个语句块计算面积。这两个语句块和我们前面提到的计算机例子中使用的语句块本质上是一样的。留意注释是如何使代码变得更加可读的。编程的第一条戒律就是:“你应当注释!”,这才能使计算机更好地执行命令。
6. 循环
顺序执行和条件执行仅仅是程序设计三个基本语句块架构方式中的两个,第三个则是循环执行,或者说是重复执行。
python有两种循环类型:while循环和for循环。for循环大概是最简单的。举个例子:
它的意思是:对于列表"spam", "eggs", "tomatoes"中的每个元素,都打印出你喜欢它。循环中的语句块为每个元素执行一次,而且每次执行,当前的元素都被赋给变量food(在这个例子中)。另外一个例子:
函数range返回给定范围的数字列表(包括第一个数字,不包括最后一个……这个例子中是[1……99])。所以,这样解释它:循环体为1(包括)到100(不包括)之间的数字每个执行一次。
7. 深入函数
我们构建函数时使用的萃取方法称为过程抽象,许多编程语言把关键字过程同函数一样使用。事实上,这两个概念是不一样的,但是在python中它们都被称为函数,因为它们或多或少以同样的方式定义和使用。
函数和过程(在其它语言中)的区别在哪里呢?函数可以返回一个值,但是过程并不返回这样的值。许多时候,用这种方法把函数划分为两种类型返回值的和不返回值的是很有用的。
不返回值的函数(过程)可以用作子程序或例行程序。我们调用这些函数,可以在很多地方使用这个函数而不需要重写它的代码。这被称为代码再利用,以后你还会知道,它意义不仅仅在这里。
这样的函数(或过程)的另一个有用性体现在它改变了环境,让我们看个例子:
打印出内容是它一方面的作用,因为这是这个函数唯一需要做的事,它其实是一个典型的所谓过程。但是事实上没有改变它的运行环境,让我们试一下怎样才能让它发生改变:
你发现错在哪儿了吗?错在函数setAge创建了它自己的也被命名为age的局部变量,它只在setAge函数内部可用。那如何才可以避免出现这个问题呢?我们可以使用全局变量。
注意:全局变量在python中不常用。它们容易引起不好的代码组织结构,被称为意大利面代码。我这里使用它们是为了引出更复杂一点的技术问题,因此请尽量避免使用它们。
通过告诉解释器一个变量是全局的(用象global age这样的表达式做),我们事实上告诉了它在函数之外使用这个变量,而不是重新创建一个新的局部变量。所以和局部相反它是全局的。)因此上面的程序可以象这样重写:
了解对象后,你会发现更好的解决这个问题的办法是使用一个有age属性和setAge方法的对象。在数据结构那段,你也将会发现一些函数改变它的环境的更好的例子。
好了--那么真正的函数是什么样?什么是函数呢,事实上?数学函数象一种“机器”,获得输入然后计算结果。它会每次返回同样的结果,如果每次提供它同样的输入。
例如:
这和数学上的函数f(x)=x*x 是一样,它的行为象一个精确的函数,仅仅依赖于它的输入,在任何情况下都不改变它的环境。
所以,我这里描绘了两种构造函数的方法:一种类型更象是过程,不返回任何结果。另一种更象是数学上的函数,(几乎)什么也不做就是为了返回一个结果。当然,在这两种极端事物之间做某些事情是可能的,尽管当函数改变事物的时候,它应该清楚它改变了。你可以通过标记它们的名字区分它们,例如为“纯粹”的函数使用象square这样的名词,而对类似过程那样的函数使用象setAge这样命令式的名字。
9. 更多类型-数据结构
现在的你已经知道了不少,那么怎样输入输出,怎样设计复杂的运算法则(程序)来执行数学运算呢?截止目前我们都在程序中使用了哪些成份呢?数字和字符串,对不对?现在让我们引入两三个其它的成份来让事情变得更有意思些。
在循环那段我提到了列表,但没真正描述它。这里将要说的就是你如何创建它。我们只需要列出元素,用逗号分开,再加上方括号就行了。
来看一个计算素数(只能被1和它本身整除的数)的例子:
这个例子提到新的东西,内建函数range事实上返回一个列表,可以象所有其它列表那样使用。(它包括第一个数,但是不包括最后一个数。)
列表可以当作逻辑变量使用。如果它非空,则为true,否则为false。因此,while candidates意思是“while名称为candidates的列表非空时”或者简单的说“while存在candidates时”。
你可以用if someElement in somelist来检查一个元素是否在列表中。
你可以用someList.remove(someElement)来删除someList中的someElement。
你可以用someList.append(something)为一个列表添加元素。事实上,你也可以使用“+”(象someList = someList+[something])。但是效率不是太高。
你可以通过在列表名之后加上用括号括起来的表示某元素位置的数字(很奇怪,列表的第1个元素,位置是0)来获得列表的某个元素。因此someList[3]是someList列表的第四个元素(依次类推)。
你可以使用关键字del删除变量。它也可以用来删除列表中的元素(就象这里)。因此del someList[0]删除someList 列表中的第一个元素。如果删除前列表是[1, 2,3],删除后就变成了[2, 3]。
在继续叙述索引列表中的元素之前,我简单解释一下上面的例子。这是古老算术的一个版本,称为“The Sieve of Erastothenes”(类似这样)。它考量一系列给定数字(在本例中是一个列表),然后有组织的删除已知不是素数的数字。只要看看它们是不是可以被分解为其它两个数就可以了。
我们从一个包含数字[2...999]的候选列表开始--我们知道1是素数(事实上,它可能是也可能不是,看你问谁了),我们想得到小于1000的所有素数。(事实上,我们的候选列表是[3...999],但是2也是候选数字,因为它是我们的第一个base)。我们还有个叫result的列表,它任何时间都包含着最新的结果。最初的时候,它只包含1。我们还有个叫base的变量。每次循环,我们删除是它的倍数的数字(它总是候选列表中最小的数)。每次循环之后,我们知道剩下的最小的数是素数(因为所有可以分解的数我们都删除了)。
因此,我们把它加入result,并把它设为新的base,然后从列表里移除它(这样就不会对它重复计算了)。当候选列表为空时,result列表将包含所有的素数。
10. 继续抽象-对象和面向对象编程
现在有个比较热门的词叫做“面向对象编程”。就像本段标题暗示的那样,面向对象编程仅仅是另外一种抽象细节的方式。程序通过命名将简单的描述抽象为复杂的操作。在面向对象编程时,我们不仅可以这样对待程序,还可以把它们做为对象。
那么--在python中我们如何做呢?我们不能直接制造一个对象。举个例子,做一个菜谱来描述炉子应该是什么样?这份菜谱因此就描述了一个被我们称为炉子的一类对象。一个非常简单的炉子类可能是这样:
对象的类用关键字class定义。类的名称通常以大写字母开始,而函数和变量(还有属性和方法)的名称以小写字母开始。方法(也就是让对象知道如何去做的函数和操作)的定义没有特别,但是要在类的定义里面。所有对象的方法应当有的第一个参数叫做self(或者类似的……)原因很快就清了。
对象的属性和方法可以这样来访问:mySpam.temperature = 2 或者dilbert.be_nice()。
我能猜到上面例子中的某些东西你仍然不清楚。例如,什么是self?还有,现在我们有了对象菜谱(也就是类),我们怎样事实上构造一个对象呢?
我们先颠倒一下顺序。对象通过象引用函数那样引用类名来创建:
myOven包含了一个Oven对象,通常叫做Oven类的一个实例。假设我们也构造好了一个Spam类,那么我们可象这样做:
myOven.spam现在将包含mySpam。怎么回事?因为,我们调用一个对象的某个方法时,第一个参数,通常称为self,总是包含对象本身。(巧妙,哈!)这样,self.spam =spam这一行设置当前Oven对象的spam属性的值为参数spam。注意它们是两个不同的事物,尽管在这个例子中它们都被称为spam。
下一篇:关于java数组的返回