Instant Hacking[译文]

这是一篇简短的关于python程序设计语言的入门教程,原文在http://www.hetland.org/python/instant-hacking.php,翻着词典翻译了来!

这是一份对编程艺术的简短介绍,其中的例子是用python写成的。(如果你已经知道了该如何编程,但是想简单了解一下python,你可以查阅我的另一篇文章Instant Python。)这篇文章已经被翻译为意大利、波兰、日本、塞尔维亚以及巴西葡萄亚语等许多种语言,而且正在被翻译为韩语。(译者:当然,现在已经包括了中文版本,只是作者并不知道。)

这篇文章和如何闯入别人的计算机系统之类的东西无关。我不关注那类事情,所以请不要email问我那些东西。

注意:要使此文中的例子正确运行,你应该把它们写在一个文本文件中,然后用解释器运行;不要试图直接在交互方式下运行它们--不是所有的都可以这样运行。(不要问我和此有关的具体细节。最好查阅python文档或者email给help@python.org)。

1. 运行环境

要用python写程序,你必须先安装一个python的解释器。它可以存在于大多数平台(包括Macintosh、Unix和Windows)。更多与此有关的信息可以在python的网站上找到。你还应该有一个文本编辑器(象emacs、notepad或者类似的东西)。

2. 编程是什么?

为计算机写程序其实就是给它一系列的指令告诉它去做什么。计算机程序在某些方面就象是菜谱,指导我们如何做菜的那种。例如[1]:

假日火腿沙拉

原料:

腌泡汁:
1/4杯酸橙汁
1/4杯低钠大豆酱油
1/4杯水
1大汤匙植物油
3/4茶匙小茴香
1/2茶匙牛至
1/4茶匙热胡椒粉
2片丁香、大蒜,捣碎

沙拉:
1份(12盎司)罐装少钠午餐肉火腿切成条状
1个洋葱,切片
胡椒粉,切好的生菜
12个樱桃西红柿,切半

方法:

把腌泡汁装在有合适盖子的广口瓶里摇匀。用塑料袋装上火腿,泼上腌泡汁,封住袋口。在电冰箱里腌制30分钟。从塑料袋里取出火腿;准备2大汤匙腌泡汁,在煮锅里煮一下。加上火腿、洋葱、绿色的胡椒。烧3到4分钟直到火腿熟了为止……

当然,没有一台计算机会懂这个……而且即便是懂,大多数计算机也不可能烧制出一份沙拉。那么,我们该如何让这些变得对计算机来说更为友好一些呢?从根本上说依赖于两点:首先,我们必须以计算机可以理解的方式与之交流;其次还要和它谈论它能够做到的事情。

第一点意味着我们必须使用一种语言--一种已经为之准备好了解释器的程序设计语言,第二点意味着我们不能期望计算机为我们做一份沙拉--但是我们可以让它做数字累加或者在屏幕上打印东西之类的事情。

3. Hello……

程序设计教程有一个传统,通常以在屏幕上打印“Hello, world!”这样的程序做为开始。对python来说,这非常简单:

print "Hello, world!"

它从根本上说很象上面的菜谱(尽管要短得多!)。它告诉计算机做什么:打印“Hello, world!”。如果让它打印更多的废话该怎么做呢?很简单:

print "Hello, world!"
print "Goodbye, world!"

不比上一个难,是不是?但是不怎么有趣……我们希望它可以处理更多的元素,就象沙拉菜谱那样。那么,我们都有哪些元素呢?首先,有字符串,象“Hello, world!”,除此之外还有数字。假设我们打算让计算机为我们计算矩形的面积。我们可以给它如下的菜谱:

# The Area of a Rectangle

# Ingredients:

width = 20
height = 30

# Instructions:

area = width * height
print area

你大概可以看出它同火腿沙拉菜谱的相似性(尽管有些细微的差别)。但它是如何工作的呢?首先,以#开始的行叫做注释事实上会被计算机忽略。然而插入象这样小段的注释对于增强你程序的可读性来说是很重要的。

接下来,看起来象 foo = bar 这样的行叫做赋值。对于 width = 20 这样的情况来说就是告诉计算机从这里开始width就代表20了。它还意味着一个名字为“width”的变量从此被创建了(如果它先前已经存在,那么会被重新覆盖)。所以,我们以后使用这个变量的时候,计算机就知道了它的值。因此,

width * height

本质上同

20 * 30

一样会计算出600这个结果,然后赋给名称为“area”的变量。程序的最后一句在屏幕上打印出变量“area”的值,所以你看到这个程序运行的最终结果仅仅是

600

注意:在某些程序设计语言中,你必须在程序开始的时候告诉计算机你将会用到哪些变量(就象沙拉中的元素)--而python足够聪明,所以你可以根据需要随时创建。

4. 反馈

现在,你可以执行一些简单,或者再复杂一点的计算了。比方说,你或许打算写一段程序来计算圆形的面积而不是矩形的:

radius = 30

print radius * radius * 3.14

然而,这事实上并不比计算矩形面积的那个程序更有意思。至少在我看来是这样。它有些僵硬。如果我们看到半径为31的圆该怎么办?怎样让计算机知道?这有点象沙拉菜谱中的:“烧3到4分钟直到火腿熟了为止。”要知道何时烧熟,我们必须检查。我们需要反馈,或者提示。计算机如何知道我们圆形的半径?同样需要输入资料……我们可以做的是告诉计算机半径是多少:

radius = input("What is the radius?")
print radius * radius * 3.14

现在程序变得漂亮一些了……input是个被称为函数的东西。(很快你将学习创建你自己的函数。而input是python内建的函数。)仅仅写下

input

什么也不会做……你必须在它的后面放上一对括号。所以input()可以工作--它会简单的要求用户输入半径的长度。而上面的那个版本对用户来说也许更友好一些,因为它首先打印出了一个问题。当我们将诸如提问字符串“What is the radius?”之类的东西放在函数调用的括号中时,这个过程被称为函数的参数传递。括号中的内容被称为参数。在上个例子中我们传递了一个提问作为参数以便input知道在获得答案前应该先打印什么。

但是获得的答案如何到达radius变量呢?函数input,调用时,会返回一个值(象许多其它函数一样)。你不一定非要使用这个值,但象我们这种情况,我们要使用它。这样,下面这两个表达式有着很大的差别:

foo = input
bar = input()

foo现在包含input函数本身(所以它事实上可以象foo("What is your age?")这样使用;这被称为动态函数调用)而bar包含用户键入的值。


5. 流程

现在我们可以编写程序执行简单的任务(运算和打印)并且可以获得用户输入了。这很有用,但仍然局限在按顺序执行命令,也就是说--它们必须按照事先安排好的顺序执行。大多数火腿沙拉菜谱是象这样顺序或者线性叙述的。但是如果我们打算让计算机检查沙拉是否烧好该怎样告诉它呢?如果烧好了,那么应该从烘箱里把它取出来--否则的话,应该接着让它烧更长一段时间什么的。我们如何表达这个?

我们想做的,其实是控制程序的流程。它可以从两个方向执行--要么拿开火腿,要不继续让它留在烘箱里。我们可以选择,条件是它是否烧好。这被称为条件执行。我们可以这样写:

temperature = input("What is the temperature of the spam?")

if temperature > 50:
print "The salad is properly cooked."
else:
print "Cook the salad some more."

意思很明显:如果温度超过50(摄氏度),那么打印出信息告诉用户烧好了,否则,告诉用户再烧制一段时间。

注意:缩进在python中很重要。条件执行(还有循环执行以及函数定义--见后面)中的语句块必须被缩进(而且要缩进同等数量的空格;一个键相当于8个空格)以便解释器可以知道它们从哪里开始到哪里结束。这同时也使程序变得更加可读。

让我们回到先前的面积计算问题。能看出来这段程序做什么吗?

# Area calculation program

print "Welcome to the Area calculation program"
print "---------------------------------------"
print

# Print out the menu:
print "Please select a shape:"
print "1 Rectangle"
print "2 Circle"

#Get the user\'s choice:
shape = input("> ")

#Calculate the area:
if shape == 1:
height = input("Please enter the height: ")
width = input("Please enter the width: ")
area = height *width
print "The area is ", area
else:
radius = input("Please enter the radius: ")
area = 3.14 * (radius**2)
print "The area is ", area

这个例子中的新东西:
1. 只使用print本身将打印出一个空行
2. ==检查两个值是否相等,与=不同,后者把表达式右侧的值赋给左侧的变量。这是一个非常重要的差别!
3. **是python的幂运算符--因此半径的平方被写成radius**2
4. print能够打印出不止一个东西。只要用逗号把它们分开就可以了。(它们在输出时会用单个空格分开。)

这个程序很简单:它要一个数字,告诉它用户打算让它计算矩形或是圆形的面积。然后,使用一个if语句(条件执行)来决定应当执行哪个语句块计算面积。这两个语句块同先前面积计算例子中使用的语句块本质上是一样的。留意注释是如何使代码变得更加可读的。编程的第一条戒律就是:“你应当注释!”无论如何--它都是一个应该养成的好习惯。

练习1:

扩展上面的程序使它包括正方形面积的计算,用户只要输入它一条边的长度就可以了。做这个练习之前你需要了解一件事:如果你有两个以上的选择,你可以象这样写:

if foo == 1:
# Do something...
elif foo == 2:
# Do something else...
elif foo == 3:
# If all else fails...

这里的elif是意思为“else if”的神秘代码:)。所以,如foo等于1,做某件事;否则,如果foo等于2,那么做另外的一些事,等等。你也可以在程序中加入其它的选项--象三角形以及任意多边形。随你的便。

6. 循环

顺序执行和条件执行仅仅是程序设计三个基本语句块架构方式中的两个。第三个则是循环执行。在上个段落中我假设了一种情况,检查火腿是否烧好,但很明显它并不适用。如果下次检查时火腿仍然没烧好该怎么办?我们怎么知道需要检查多少次?事实上,我们不知道。而且我们也没必要知道。我们可以要求计算机持续检查直到烧好了为止。怎么表达这个?你猜到了--我们使用循环,或者说是重复执行。

python有两种循环类型:while循环和for循环。for循环大概是最简单的。举个例子:

for food in "spam", "eggs", "tomatoes":
print "I love", food

它的意思是:对于列表"spam", "eggs", "tomatoes"中的每个元素,都打印出你喜欢它。循环中的语句块为每个元素执行一次,而且每次执行,当前的元素都被赋给变量food(在这个例子中)。另外一个例子:

for number in range(1, 100):
print "Hello, world!"
print "Just", 100 - number, "more to go..."

print "Hello, world"
print "That was the last one... Phew!"

函数range返回给定范围的数字列表(包括第一个数字,不包括最后一个……这个例子中是[1……99])。所以,这样解释它:

循环体为1(包括)到100(不包括)之间的数字每个执行一次。(哪个是循环体以及随后的表达式事实上做什么留下来做为练习。)

但这对我们的烧菜问题并没有实质的帮助。如果我们打算检查火腿一百次,那么这是个很好的解决方案;但是我们不知道这是否够--或者太多了。我们只是希望它在温度达不到(或者,直到它足够热--大致某个状态)的时候持续检查。所以,我们使用while:

# Spam-cooking program

# Fetch the function sleep
from time import sleep

print "Please start cooking the spam. (I\'ll be back in 3 minutes.)"

# Wait for 3 minutes (that is, 3*60 seconds)...
sleep(180)

print "I\'m baaack :)"

# How hot is hot enough?
hot_enough = 50

temperature = input("How hot is the spam?")
while temperature < hot_enouth:
print "Not hot enough... Cook it a bit more..."
sleep(30)
temperature = input("OK, How hot is it now?")

print "It\'s hot enough - You\'re done!"

这个例子中的新东西……

1. 有些有用的函数被存储在模块中而且可以被导入。此例中我们从python自带的time模块中导入了函数sleep(它休止给定的多少秒的时间)。(做你自己的模块当然也是可能的……)

练习2:

写一个程序,持续从用户获得数据然后相加,直到它们的和为100。再写一个程序,从用户那里获得100个数据,打印出它们的和。

7. 大一点的程序 - 抽象化

如果想知道一本书的大致内容,你不会翻遍所有的页--你只是看看目录,是不是?它会列出书的主要内容。现在--想像写一本菜谱。许多菜谱,像“奶油火腿通心面”和“瑞士火腿馅饼”很可能包含相同的东西,比如火腿,在这种情况下--你肯定不会打算在每个菜谱里都重复叙述如何制作火腿。(好了……你事实上可能不做火腿……但是为了做例子,请忍受一下:))。你会把制作火腿的菜谱单独放在一个章节,而仅仅在其它章节里引用它。这样--代替在每个菜谱里都完整的描述,你只要引用章节的名称就可以了。在计算机编程中这被称为抽象化。

我们是不是已经象这样运行了某些东西?是的。我们没有详细的告诉计算机如何从用户那里获得一个答案(好了--我们没有真的这样做……同样地……我们也没有真正的在做火腿:))而是简单的使用了input--一个函数来代替。我们事实上可以构造我们自己的函数,来应用于这种类型的抽象化中。

假设我们希望找到小于给定正数的最大整数。例如,给定2.7,这个数应当是2。这往往被称为给定数的“底线(floor)”。(这事实上可以用python的内建函数int来处理,但是,请再次忍受我拿它作例子……)我们该怎样做?一个简单的解决办法是从0开始试每一个可能的数:

number = input("What is the number?")

floor = 0

while floor <= number:
floor = floor + 1

floor = floor - 1

print "The floor of ", number, "is ", floor

注意当floor不再小于(或者等于)给定数时循环结束了;我们加了太多1给它。因此我们必须为它减去1。如果我们希望把它应用于完整的数学运算该怎么办呢?我们不得不为求每个数的基数("floor"-ing)而写一次完整的循环。这很不舒服……你可能猜到了我们代之以什么:把它放在我们自己的函数中,命名为“floor”:

def floor(number):
result = 0
while result <= number:
result = result + 1
result = result - 1
return result

这个例子中的新东西……

1. 函数用关键字def定义,函数名紧随其后并且要用括号把需要的参数括起来。
2. 如果要求函数返回一个值,要使用关键字return来处理(它同时也自动结束函数定义)。

定义了函数之后,我们可以象这样使用它:

x = 2.7
y = floor(2.7)

执行后,y的值应该是2。定义拥有多个参数的函数也是可以的:

def sum(x, y):
return x + y

练习3

写一个函数,用欧几里德方法寻找两个数的一个共同因数。工作过程是这样的:

1. 假设两个数,a和b,a大于b
2. 重复以下步骤直到b变成0:
1. a变为b的值
2. b变成没有改变值之前的a除以没有改变值之前的b的余数
3. 返回a的最后一个值

提示:

* 使用a和b作为函数的参数
* 简单的设定a大于b
* x除以z的余数用表达式 x % z 来计算
* 两个变量可以象这样一起赋值:x, y = y, y+1。这里x被赋以值y(这意味着,y的值此前已经指定)而且y被递增了1。
--------------------------------------------------------------------------------

-- 作者:自由精灵
-- 发布时间:2006-2-10 11:57:49

--
8. 深入函数

上面的练习怎么做?难吗?还不太清楚函数?别担心--我还没完成我的话题呢。

我们构建函数时使用的萃取方法称为过程抽象,许多编程语言把关键字过程同函数一样使用。事实上,这两个概念是不一样的,但是在python中它们都被称为函数(因为它们或多或少以同样的方式定义和使用)。

函数和过程(在其它语言中)的区别在哪里呢?嗯--就像你在前面的段落里看到的那样,函数可以返回一个值。区别就是过程并不返回这样的值。许多时候,用这种方法把函数划分为两种类型--返回值的和不返回值的--是很有用的。

不返回值的函数(过程)可以用作子程序或例行程序。我们调用这些函数,它们制造某些原料,就象泡沫鲜奶之类的。我们可以在很多地方使用这个函数而不需要重写它的代码(这被称为代码再利用--以后你还会知道,它意义不仅仅在这里)。

这样的函数(或过程)的另一个有用性体现在--它改变了环境(例如,把糖和奶油混在一起搅拌,它们的整个外部状态就变化了)让我们看个例子:

def hello(who):
print "Hello, ", who

hello("world")
# Prints out "Hello, world"

打印出内容是它一方面的作用,因为这是这个函数唯一需要做的事,它其实是一个典型的所谓过程。但是……它事实上没有改变它的运行环境,是不是?它怎样才能改变呢?让我们试一下:

# The *wrong* way of doing it
age = 0

def setAge(a):
age = a

setAge(100)
print age
# Prints "0"

错在哪儿?错在函数setAge创建了它自己的也被命名为age的局部变量,它只在setAge函数内部可用。那如何才可以避免出现这个问题呢?我们可以使用全局变量。

注意:全局变量在python中不常用。它们容易引起不好的代码组织结构,被称为意大利面代码。我这里使用它们是为了引出更复杂一点的技术问题--如果你可以请尽量避免使用它们。

通过告诉解释器一个变量是全局的(用象global age这样的表达式做),我们事实上
告诉了它在函数之外使用这个变量,而不是重新创建一个新的局部变量。(所以,和局部
相反它是全局的。)因此上面的程序可以象这样重写:

# The correct, but not-so-good way of doing it
age=0

def setAge(a):
global age

setAge(100)
print age

# Prints "100"

了解对象(随后谈到)后,你会发现更好的解决这个问题的办法是使用一个有age属
性和setAge方法的对象。在数据结构那段,你也将会发现一些函数改变它的环境的更好的
例子。
好了--那么真正的函数是什么样?什么是函数呢,事实上?数学函数象一种“机
器”,获得输入然后计算结果。它会每次返回同样的结果,如果每次提供它同样的输入。
例如:

def square(x):
return x*x

这和数学上的函数f(x)=x*x 一样。它的行为象一个精确的函数,仅仅依赖于它的输
入,在任何情况下都不改变它的环境。

所以--我这里描绘了两种构造函数的方法:一种类型更象是过程,不返回任何结
果;另一种更象是数学上的函数,(几乎)什么也不做就是为了返回一个结果。当然,在
这两种极端事物之间做某些事情是可能的,尽管当函数改变事物的时候,它应该清楚它改
变了。你可以通过标记它们的名字区分它们,例如为“纯粹”的函数使用象square这样的
名词而对类似过程那样的函数使用象setAge这样命令式的名字。

9. 更多类型-数据结构

现在--你已经知道了不少:怎样输入输出,怎样设计复杂的运算法则(程序)来执
行数学运算,但是好戏还在后头呢。

截止目前我们都在程序中使用了哪些成份呢?数字和字符串,对不对?没意思的种
类……现在让我们引入两三个其它的成份来让事情变得更有意思些。

数据结构是种组织数据的成份。(惊奇,吃惊……)单个的数据没有什么真正的数据
结构,是不是?但是假设我们需要很多数放在一起做为一个成份--那就需要某种结构。
例如,我们可能想要一个数据列表。那很容易:

[3, 6, 78, 93]

在循环那段我提到了列表,但没真正描述它。好--这里说的就是你如何创建它。只
需要列出元素,用逗号分开,再加上方括号就行了。

来看一个计算素数(只能被1和它本身整除的数)的例子:

# Calculate all the primes below 1000
# (Not the best way to do it, but...)

result = [1]
candidates = range(3, 1000)
base = 2
product = base

while candidates:
while product < 1000:
if product in candidates:
candidates.remove(product)
product = product+base
result.append(base)
base = candidates[0]
product = base
del candidates[0]
result.append(base)
print result

这个例子中的新东西……

内建函数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列表将包含所有的素数。精巧吧,哈!
思考一下:第一次循环有什么特别吗?那时base 是2,但它一样经过了筛选。为什
么?为什么这不发生在其它的base值身上?我们打算移除product时能否确定它在候选列
表中呢?为什么?

接下来是什么呢?哦,是的……索引。还有切片。它们是从python列表中获得单个
元素的方法。你已经见到了普通的索引行为。它相当简单。事实上,我已经告诉了你所有
你需要知道的关于它的东西,除了一件事:负数索引从列表的末尾向前计算。所以,
someList[-1]是someList的最后一个元素,someList[-2]是它之前的一个元素,依次类
推。

切片,仍然,对你来说是陌生的。它和索引相似,除了切片可以获得列表中的所有的
元素,而不仅仅是单个的元素。这如何做呢?象这样:

food = [“spam”, “spam”, “eggs”, “sausages”, “spam”]
print food[2:4]

# Prints “[\'eggs\', \'sausages\']”

10. 继续抽象-对象和面向对象编程

现在有个比较热门的词叫做“面向对象编程”。

就象本段标题暗示的那样,面向对象编程仅仅是另外一种抽象细节的方式。程序通过
命名将简单的描述抽象为复杂的操作。在面向对象编程时,我们不仅可以这样对待程序,
还可以把它们做为对象。(现在,这肯定会让你吃惊,哈!)例如,如果编写烧火腿程
序,我们不用编写很多过程来处理温度、时间、成份等等,我们可以把它们结合为一个火
腿对象。或者,也许我们可以再有炉子对象和时钟对象……那么,象温度这类事物就变成
了火腿对象的一个属性,而时间可以从时钟对象读取。要使用我们的程序做某些事,我们
可以教给我们的对象某些方法;比如,炉子应当知道如何烹制火腿等。

那么--在python中我们如何做呢?我们不能直接制造一个对象。不能直接制造一个
炉子,而是做一个菜谱来描述炉子应该是什么样。这份菜谱因此就描述了一个被我们称为
炉子的一类对象。一个非常简单的炉子类可能是这样:

class Oven:

def insertSpam(self, spam):
self.spam = spam

def getSpam(self):
return self.spam

这看起来很难理解,还是怎样呢?

这个例子中的新东西……

对象的类用关键字class定义。

类的名称通常以大写字母开始,而函数和变量(还有属性和方法)的名称以小写字
母开始。

方法(也就是让对象知道如何去做的函数和操作)的定义没有特别,但是要在类的
定义里面。

所有对象的方法应当有的第一个参数叫做self(或者类似的……)原因很快就清楚
了。

对象的属性和方法可以这样来访问:mySpam.temperature = 2 或者dilbert.be_nice
()。

我能猜到上面例子中的某些东西你仍然不清楚。例如,什么是self?还有,现在我们
有了对象菜谱(也就是类),我们怎样事实上构造一个对象呢?

我们先颠倒一下顺序。对象通过象引用函数那样引用类名来创建:

myOven = Oven()

myOven包含了一个Oven对象,通常叫做Oven类的一个实例。假设我们也构造好了
一个Spam类,那么我们可象这样做:

mySpam = Spam()
myOven.insertSpam(mySpam)

myOven.spam现在将包含mySpam。怎么回事?因为,我们调用一个对象的某个方法
时,第一个参数,通常称为self,总是包含对象本身。(巧妙,哈!)这样,self.spam =spam这一行设置当前Oven对象的spam属性的值为参数spam。注意它们是两个不同的事物,尽管在这个例子中它们都被称为spam。

11. 练习3答案

这是这个运算法则的一个非常简洁的版本:
def euclid(a, b):
while b:
a, b = b, a%b
return a

12. 参考
[1]假日火腿沙拉菜谱摘自«Hormel Foods»电子版菜谱。
Copyright © Magnus Lie Hetland

Posted on February 23, 2006 1:34 PM | | Comments (0) | TrackBacks (0)

Python 手册

Posted on February 23, 2006 12:08 PM | | Comments (0) | TrackBacks (0)

servlet的另类写法:使用jython众所周知,servlet是java在服务器端程序设计的重要组成部分,servlet执行效率较高,适合做程序的逻辑处理,缺点是使用起来比较繁琐,每个servlet必须在web.xml中声明并且当修改部署后的servet后一般还需要reload容器,这种情况对于开速开发是很不利的,本文介绍一种另外的serlvet开发方式:使用jython 本文假设读者熟悉某种应用服务器(如tomcat),java 和 python程序设计语言 在http://www.jython.org上下载jython,假设为D:jython-2.1 建立一个名称为jython的web应用,把D:jython-2.1jython.jar copy到webapp下的WEB-INFlib下,并配置web.xml如下: PyServlet org.python.util.PyServlet 11
python.home
D:jython-2.1

python.path
D:jython-2.1Lib
PyServlet *.py 现在编写一个HelloWorld.py,如下: from javax.servlet.http import HttpServlet class HelloWorld(HttpServlet):#需要类名和文件名一致 def doGet(self,request,response): response.setContentType("text/html;charset=utf-8"); out = response.getWriter() print>>out,"

Hello World by Jython Servlet!


"; 启动应用服务器,假设位于localhost,端口8080,访问http://localhost:8080/jython/HelloWorld.py,是不是看到了“Hello World by Jython Servlet!”字符串输出呢? 正如您所想象的一样,jython servlet无须在web.xml做任何声明,PySerlvet会拦截所有.py请求并分发给各自的jython servlet,并且在修改HelloWorld的实现后,立刻就可以看到变化,无须重启应用服务器,很方便吧! 注:编写HelloWorld.py的时候请严格缩进,python是一种以缩进区分代码块的语言,不正确的缩进是语法错误

Posted on February 23, 2006 11:40 AM | | Comments (1) | TrackBacks (0)

Jython 是 Python 编程语言的 100% 纯 Java 实现,它结合了 Python 及 Java 虚拟机和库的优点,并作为 Java 平台的方便补充。本文中,通过比照 Python 和 Java 语言创建类的方法,以及它们如何使用解释器,软件顾问兼 developerWorks 固定投稿人 Uche Ogbuji 向 Java 开发人员介绍了 Jython 2.1。通过提供 Java 库访问的样本,以及 Jython 解释器 shell 和代码文件,Uche 说明了其中的差别。 最初 Jython 称为 JPython,是一个十足的 Java 应用程序,它允许开发人员使用 Python 编程语言的语法和大多数特性。Java 程序员对 Jython 感兴趣的原因有几个: Python 解释器 shell 的 Jython 版本可以对某些想法和 API 作便利的实验和研究,而不必经历一般的 Java 编译/运行周期。 Python 被设计成动态且通用的,因此您不必通过使用复杂的库(诸如那些用于 Java 反射和内省的库)来添加这些特性。这使某些开发更简单,而且它在自动测试框架中特别有用。 许多开发人员喜欢 Python 的语法和这种语言所带来的感觉;他们发现它是开发和维护 Java 应用程序的生产率更高得多的方法。 本文中,我提供了一些访问 Java 库、使用 Jython 解释器 shell 及显示 Jython 代码文件示例,以介绍 Jython 2.1,它是最新的发行版。要理解本文,您不必了解 Python,不过如果您计划进一步使用 Jython,而不是仅局限于了解本文的基本示例,那么就必须学习 Python 语言。 我使用的环境是 Red Hat 8.0(2.4.18 内核)和 J2SE 1.4.0。在 Jython 站点(请参阅参考资料)上,查看特定于 Jython 平台的说明,以获取有关 Jython 用户可以选择的平台和 Java 环境的更多信息。 注:Jython 语言和 Java 语言都在 Java 运行时上操作。 Jython 入门 Jython 是作为包含安装程序的单个 Java 类文件而发布的。只要下载 jython-21.class 并将该文件放在 CLASSPATH 内,然后运行 java jython-21。选择您要安装的组件(在示例中,我选择了所有缺省组件),接受其许可证(这是开放源码 BeOpen/CNRI 许可证)并指定安装目录,之后安装程序会完成其余安装。 如果安装时碰到问题,请参阅 Jython 网站上的安装信息页。对于 UNIX 平台,您可能想将选择的 Jython 安装路径添加到 PATH 环境变量。现在只要输入“jython”就可以运行交互式 shell: 清单 1. 运行 Jython shell $ jython Jython 2.1 on java1.4.0_01 (JIT: null) Type "copyright", "credits" or "license" for more information. >>> >>> 提示符允许您输入命令并立即获得结果。在 Java 编程中,每个程序都必须至少定义一个类。清单 2 演示了一个完整的 Java 程序,它用于将一条消息写到屏幕: 清单 2. 完整的 Java 程序 class App { public static void main(String args[]) { System.out.println("I don't like spam!"); } } JPython 将这些行减少为: 清单 3. Jython 减少了 Java 代码开销 >>> print "I don't like spam!" I don't like spam! >>> print 关键字是重要的工具之一,特别是在交互式 shell 中,它会立即打印其参数,并随后向您返回 shell 提示符。不仅输入和调试的代码比较少,而且不必经历编译/运行周期就可以马上获得结果。可以一次打印多个值,并象下面显示的那样轻松使用简单的表达式: 清单 4. print 是一个重要的 Jython 工具 >>> print "one plus one is", 1+1 one plus one is 2 >>> Jython 表达式类似于 Java 表达式。1+1 的结果是一个整数,通过 print 它被强制转换成字符串,并被并置到由逗号定界符指定的初始字符串。 通过使用 Jython,您甚至不需要什么工具就可以访问标准 Java 库。以下示例访问了 java.util.Random: 清单 5. 通过 Jython 访问标准 Java 库 >>> from java.util import Random >>> rng = Random() >>> i = rng.nextBoolean() >>> print i 1 >>> Jython 的 import 关键字与 Java 语言版本的相似之处在于它使一个模块中的内容可以为其它模块所使用,但是语法和行为有所差别。 上面清单 5 中的示例使用了相关的 from 关键字以限制从 java.util 导入哪些符号。其后一行显示了 Random 类实例的创建。正如您所看到的,不需要 new 关键字。 也不需要对保存新实例的变量进行任何类型说明。这强调了 Jython 的一个重要简化,而且这是其动态本性的一个优点 — 您不必再担心数据类型定义了。 清单 5 中的下一行演示了方法调用,这与 Java 语言完全相同,只是没有对结果进行类型声明。Java 代码中的 nextBoolean() 是布尔值。Jython 2.1 没有布尔类型(不过这可能会很快改变;Python 2.3 就添加了布尔类型),因此它替换为 0 或 1 这样的整数。类似地,要调用希望使用布尔值的 Java 方法,就要传递满足这些约束的整数值。 您也可以使用 import 关键字对导入的所有符号作全限定,如清单 6 所示: 清单 6. Import 对所有导入的符号名称作了全限定 >>> import java.util.Random >>> rng = java.util.Random() >>> print rng.nextFloat() 0.9567907452583313 >>> Jython 的浮点值与 Java 语言中的完全相同。 直接在源文件中编写代码 Jython 解释器对于快速检查和作提示都很方便,但您不必在这其中完成所有工作 — Jython 还允许您在源文件中编写代码,并随后运行该代码(虽然使用 Jython 时,编译步骤是可选的)。以下清单是一个独立 Jython 程序的示例: 清单 7. 模拟硬币投掷的样本 Jython 程序(保存在名为 listing7.py 的文件中) from java.util import Random rng = Random() #This is a comment in Jython print "Flipping a coin..." if rng.nextBoolean(): print "Came up heads" else: print "Came up tails" 在解释如何运行该代码之前,让我们先解释一下代码。这个示例引入了 Jython 中的 if 语句,这是有些人对 Jython(及其先辈 Python)评论的首要方面之一。没有字符定界符标记出当 if 语句条件为真时要执行的代码块(Jython 中的条件不需要括起的圆括号,这与 Java 编程一样)。只是这些代码比周围的代码缩进一层。 Jython 代码块总是使用缩进进行标记,而不是使用其它标记,例如花括号。引入代码块的语句(例如 if)以冒号作为结尾。Jython 的这个特性意味着在编写代码时必须小心,因为缩进代码的方式实际上可能会改变代码的含义。例如,清单 8a 产生的打印输出只有数字 3,因为它上面两个语句都属于其条件永远不为真的 if 块: 清单 8a. 缩进:只打印“3” if 0: print "1" print "2" print "3" 如果我只更改其中一行的缩进,那么就会打印数字 2 和数字 3: 清单 8b. 缩进:打印“2”和“3” if 0: print "1" print "2" print "3" 缩进还必须一致,它必须与将代码组织成块的语句相关联,而且通常它还必须控制代码流。例如: 清单 8c. 缩进:语法错误 print "1" print "2" print "3" 这只会产生一个语法错误,因为没有任何控制语句要求将一个块与代码其余部分相分离。 使用缩进标记代码块是 Python 和 Jython 的更有争议的特性之一,但我认为这个问题常常被夸大了。毕竟,如果您遵循有关缩进的良好的编码标准,就不应该有这种问题。如果遵循了良好的编码缩进,那么机器会执行,同行评论家就无话可说,因此事实胜于雄辩。 此外,我知道当开发人员对这种语言使用一段时间后,没有谁会注意这种限制。适当缩进成为 Jython 的第二本性。缩进和语法之间的这种联系当然可能会引起以前未遇到过的错误,但是没有显式的定界符也消除了使用这些定界符的语言中的某些常见错误。 您可以不必编译就可运行清单 7 中的文件(listing7.py),只需将该文件名作为 jython 命令的参数来调用,如下所示: 清单 9. 不编译就运行“硬币投掷” $ jython listing7.py Flipping a coin... Came up tails $ 在上个示例中,$ 就是 UNIX shell 提示符,它非常象 Windows 系统上的 C:\>。您还可以使用 jpythonc 命令将模块编译成 Java 字节码(.class)文件,该命令允许您使用 java 或 jre 命令直接运行它。用这种方法编译的 Jython 模块有一些限制,但这个问题超出了本文的范围。 构建全局函数 即使 Java 语言不支持全局函数,您也可以用 Jython 轻松创建全局函数。您还可以定义全局变量(通常要设置常量,而不必为它们创建类包装器)。例如,看一下下面的清单: 清单 10. 全局函数以字符串形式返回一系列数字(保存在名为 listing10.py 的文件中) START = 1 SPACER = " " def CounterString(length): buffer = "" for i in range(START, length): buffer = buffer + str(i) + SPACER return buffer print CounterString(10) 首先我们定义了两个全局变量 START 和 SPACER,它们用作该程序的常量,其中一个是整数,而另一个是字符串。 接着我们使用 def 关键字定义了函数 CounterString。该函数有一个称为 length 的整数参数。Jython 未显式检查该参数是否是整数,这一事实是 Jython 的动态特性的一个优点;但它同时也可能是一个缺点,因为某些类型错误只有在后面的 Java 编程中才能被捕获。 请注意函数特征符行以冒号结尾,从而引入了一个新块,它是通过使后续行缩进来标记的。这一新块的第一行将字符串 buffer 初始化为空字符串。对这个 buffer 进行操作以产生所期望的函数结果。 下一行创建了一个循环。Jython 的 for 语句与 Java 语言语句完全不同。在 Java 编程中,您可以设置初始和终止条件,以及每个循环步骤。Jython 的循环自始至终总使用一个特殊序列。该序列一般是一个列表,它是 Jython 的一种非常重要的数据类型。 由三个字符串组成的列表如下所示: ["a", b", "c"] 如果您想对从 1 到 N 的数字作循环(如同我们这里所做的),那么可以使用函数 range(),它返回给定范围内的数字列表。在交互式 Jython 提示符下做些实验应该会帮助您熟悉这个工具: 清单 11. range() 函数示例 >>> range(5) [0, 1, 2, 3, 4] >>> range(1, 5) [1, 2, 3, 4] >>> range(1, 10, 2) [1, 3, 5, 7, 9] >>> range(10, 1, -3) [10, 7, 4] 回过头看一下清单 10,for 循环的每个迭代都作为一个代码块运行,该代码块从该函数体其余部分缩进一层。该块是将当前 buffer 并置到新数字的一行代码,首先使用 str() 函数(而不是 Java 编程中的 cast)将新数字强制转换成字符串,随后追加一个分隔符。该循环终止后,返回最终的 buffer。该函数体之后的一行代码对它进行测试。Jython 同样允许您不使用任何特殊工具(如应用程序类上的 main 方法)就可以完成这个任务。清单 10 的输出显示如下: 清单 12. 清单 10 的输出 $ jython listing10.py 1 2 3 4 5 6 7 8 9 构建类与构建函数一样容易 用 Jython 创建类与创建全局函数一样容易。清单 13 提供了一个示例: 清单 13. 用户定义的类的简单示例(保存在名为 listing13.py 的文件中) class Dog: def __init__(self, bark_text): self.bark_text = bark_text return def bark(self): print self.bark_text return def annoy_neighbors(self, degree): for i in range(degree): print self.bark_text return print "Fido is born" fido = Dog("Bow wow") print "Let's hear from Fido" fido.bark() print "Time to annoy the neighbors" fido.annoy_neighbors(5) 上述代码中,第一行命名该类,其定义完全是一个大的代码块。 定义的第一个方法是特殊的初始化程序(类似于 Java 构造函数)。它总是命名为 __init__,而且每当创建该类的新实例时就调用它。在 Jython 中,将正被调用(或在初始化程序的情况中,被创建)的当前实例显式声明为参数。传统上这个参数称为 self。 在 Dog 初始化程序中,bark_text 参数是一个字符串,通过使用 self 将它存储为实例变量。在调用方法 bark() 时不采用任何显式参数,但仍须指定 self。 方法 annoy_neighbors 确实采用了一个显式参数,它是除了 self 之外指定的另一个参数,并且它是狗为了烦扰邻居而叫嚷的次数。请注意代码运行时很容易进入深度嵌套,因此要进行缩进。在该类定义的方法 annoy_neighbors 定义内有一个循环块。以 print "Fido is born" 开始的代码再次演示了该类。清单 13 的输出类似如下: 清单 14. 清单 13 的输出 $ jython listing13.py Fido is born Let's hear from Fido Bow wow Time to annoy the neighbors Bow wow Bow wow Bow wow Bow wow Bow wow 把各种编程语言联系起来 本文中,我们仅简要描述了将 Jython 添加到 Java 编程库的优点: Jython 语言减少了执行任务所需的代码量。 Jython 解释器允许您不编译就可以运行代码,从而有助于加速代码开发。 它允许您构建 Java 语言不支持的全局函数和变量。 它引入了动态的类型定义,同时在静态类型定义的虚拟机中使用类型推断和数据类型转换来正确操作。 它引入了泛型数据类型的使用(尽管诸如 Tiger 这样即将发布的 Java 版本也已引入了泛型类型)。 它允许开发人员轻松开发自动的测试框架。 通过一系列示例,我们还介绍了开发人员应该知道的语法和类型定义方面的某些差别,包括 Jython 中缩进的语法含义以及引入整数以替代当前不受支持的布尔类型。 使用 Jython 绝不是要您抛弃 Java 语言。Jython 会是一个非常方便的补充,对于快速检查和构造原型、测试以及处理它的方法比较适合的编码任务的选择很有用。 参考资料 请访问 Jython 主页以下载其实现并了解如何使用它的更多信息;如果您已获得了 Jython,那么尝试用这些参考资料来协助安装和解决特定于平台的问题。 Jython 是 Python 语言的实现;如果您计划使用 Jython,那么要熟悉 Python.org 上发布的文档及其它参考资料。 developerWorks Linux 专区有一个用于 Python 的固定专栏 Python 编程系列。 如果您是 Java 初学者,那么“Java 语言基础 ”教程(developerWorks,2000 年 11 月)通过一些示例演示面向对象框架和标准编程实践中 Java 编程语言的语法,介绍了该语言。(本文中的有些示例就是基于该教程中的示例。) 在“诊断 Java 代码:Repl 提供交互式评价”(developerWorks,2002 年 3 月)中,Eric Allen 提供了一个使用 Jython 构建优雅的 repl 的示例,它是“读取-求值-打印-循环(read-eval-print-loop)”。 加入到 Jython 用户邮件列表,这是获得在线帮助的极佳所在,可以和开发人员交互地讨论 Jython。 O'Reilly 和 Noel Rappin 提供的“Tips for Scripting Java with Jython, Part 1”包含了 Jython 的 11 个特定特性,对于 Java 程序员来说,它们特别省时或令人兴奋。 Jython Essentials(Samuele Pedroni 和 Noel Rappin 编写,由 O'Reilly 于 2002 年 3 月出版)向 Jython 程序员提供了对 Jython 的完全的介绍、大量 Jython/Java 交互示例以及有关使用模块和库的参考资料。(可以在线获得 Chapter 1。) 在 Python Programming with the Java Class Libraries(Richard Hightower 编写,由 Addison-Wesley/Pearson 于 2002 年 6 月出版)中了解如何用 Jython 构建 Web 和企业应用程序。 ActiveState Programmer Network 提供了两个 Jython 参考资料:用 Jython 实现的一个简单 JSP 定制标记和一个简单的 Jython servlet。 Fourthought Inc. 是一家专门从事企业知识管理的 XML 解决方案的软件供应商和咨询公司。Fourthought 开发了 4Suite,这是一个用于 XML、RDF 和知识管理应用程序的开放源码平台。 使用 Python 开发 Web 服务的这个综合文章集可以帮助您理解 Jython 开发工作。 在 developerWorks Java 技术专区查找成百篇与 Java 技术相关的其它文章。

Posted on February 23, 2006 11:28 AM | | Comments (0) | TrackBacks (0)

http://jydt.sourceforge.net/ Introduction JyDT for Eclipse aims to provide the development tools expected by a Jython developer. The project was started by Red Robin in November 2003 and was carried out with limited resources. The project has gone open source in July 2005. The evolution of the plug-in is driven by the needs of the users. The current version of JyDT is 1.4.14. See the release history, the list of all stories and the development page for details about the development of the plug-in. The current version supports Jython 2.1 on Eclipse 3.1.x. It cannot be used on Eclipse 3.2Mx yet. It is available as a file release on the project web pages on SourceForge. For older versions of Eclipse, use the following versions of JyDT. They are not available as file releases on SourceForge, because they were closed source. Eclipse 2.1.x JyDT 1.2.16 Download Eclipse 3.0.x JyDT 1.4.10 Download Notifications of new releases and informative messages are sent on the JyDT Users mailing list. Register yourself if you want to be kept up-to-date on the evolution of JyDT. Send experience reports, comments, suggestions, questions and bug reports using the appropriate means on the project web page at SourceForge: the tracker and the public forums. The documentation provided on this web site always refers to the latest version of the plug-in. For documentation on features in older versions, please read the help pages included in those versions. Browse the features of the plug-in listed on the left side. New users should start with the installation instructions. After installation it is a good idea to check the getting started section to check correct configuration of the plug-in according to your preferences. Due to the iterative style of development, in which many small releases are preferred over little big releases to ensure a short feedback cycle, it is possible that features are either not, not yet or only partially implemented. Before sending a mail with a question on missing or partially implemented features, please consult the release history, the list of all stories, the bug tracker and the frequently asked questions to check whether your issue is already known. If the issue is not known, send a bug report.

Posted on February 23, 2006 11:24 AM | | Comments (0) | TrackBacks (0)
上一页 1 2 3 4 5 6 7 下一页
相关内容
广告计划
最新评论
[评论] 鸿雁 : 默默地为他们祈祷吧
[评论] lym328 : 客源CRM非常不错-----如有需要可以了解
[评论] kevinwu : 作用肯定是有的,Google会首先搜索站
[评论] ss : 其实还真的感觉不到sitemap的作用~
[评论] kevinwu : 谢谢你的关注 :-)
[评论] h51h : 贵博客写得非常的好,界面简洁但内
[评论] snguo : 这里很好 来这里支持下呢?
[评论] redondo : 感谢你分享知识! 这篇文章我转载到
[评论] kevinwu : 就是这本;看来我买的贵了点 - 8折;我
[评论] 安妮 : 《Flex3.0 RIA开发详解:基于ActionScript3.0