learnpython09.docx

上传人:b****7 文档编号:10549855 上传时间:2023-02-21 格式:DOCX 页数:57 大小:43.29KB
下载 相关 举报
learnpython09.docx_第1页
第1页 / 共57页
learnpython09.docx_第2页
第2页 / 共57页
learnpython09.docx_第3页
第3页 / 共57页
learnpython09.docx_第4页
第4页 / 共57页
learnpython09.docx_第5页
第5页 / 共57页
点击查看更多>>
下载资源
资源描述

learnpython09.docx

《learnpython09.docx》由会员分享,可在线阅读,更多相关《learnpython09.docx(57页珍藏版)》请在冰豆网上搜索。

learnpython09.docx

learnpython09

272

第九章

用Python完成

常见的任务

本章内容:

●数据结构操作

●文件操作

●操作程序

●与Internet相关的任务

●较大的例子

●练习

现在,我们已学习了Python的语法,它的基本的数据类型,和很多我们喜欢的

Python的库函数。

本章假设你至少理解了这门语言的所有基本成分,并且除了

Python的优雅和“酷”的方面外,也了解了它实用的方面。

我们将介绍Python程

序员要面对的常见的任务。

这些任务分为——数据结构操作,文件操作等等。

数据结构操作

Python的最大的特点之一是它把列表、元组和字典作为内置类型。

它们非常灵活

和容易使用,一旦你开始使用它们,你将发现你会不由自主地想到它们。

内嵌(inline)拷贝

由于Python引用的管理模式,语句a=b并没有对b引用的对象作拷贝,而只

是对那个对象产生了新的引用。

有时需要一个对象的新的拷贝,而不只是共享一

个引用。

怎样做到这一点依赖于对象的类型。

拷贝列表和元组的最简单的方式有

点奇怪。

如果myList是一个列表,那么要对它做拷贝,你可以用:

newList=myList[:

]

你可以理解为“从开始到结尾的分片”,因为我们在第二章“类型和操作符”里学

用Python完成常见的任务

273

到,一个分片开始的缺省索引是序列的开始(0),而缺省的结尾是序列的结尾。

由于元组支持同样的分片操作,这个技术也适用于拷贝元组。

而字典却不支持分

片操作。

为了拷贝字典myDict,你可以用:

newDict={}

forkeyinmyDict.keys():

newDict[key]=myDict[key]

这个操作很常见,所以在Python1.5里为字典对象增加了一个新方法来完成这个

任务,就是copy()方法。

所以前面的代码可以替换为一句话:

newDict=myDict.copy()

另一个常见的字典操作现在也是标准的字典特性了。

如果你有一个字典oneDict,

而想用另一个不同的字典otherDict的内容替换它,只需要用:

oneDict.update(otherDict)

这与下面的代码相同:

forkeyinotherDict.keys():

oneDict[key]=otherDict[key]

如果在update()操作前oneDict与otherDict共享一些键时,在oneDict中的

键关联的旧值将被删除掉。

这也许是你所想要的(通常是这样,这也是为什么选

择这个操作并称之为update()的原因)。

如果这不是你期望的,那么要做的也许

是抱怨(引发异常),如下:

defmergeWithoutOverlap(oneDict,otherDict):

newDict=oneDict.copy()

forkeyinotherDict.keys():

ifkeyinoneDict.keys():

raiseValueError,"thetwodictionariesaresharingkeys!

"

newDict[key]=otherDict[key]

returnnewDict

或者把二者的值结合为一个元组,例如:

defmergeWithOverlap(oneDict,otherDict):

newDict=oneDict.copy()

第九章

274

forkeyinotherDict.keys():

ifkeyinoneDict.keys():

newDict[key]=oneDict[key],otherDict[key]

else:

newDict[key]=otherDict[key]

returnnewDict

为了说明前面三个算法的不同,考虑下面两个字典:

phoneBook1={'michael':

'555-1212','mark':

'554-1121','emily':

'556-0091'}

phoneBook2={'latoya':

'555-1255','emily':

'667-1234}

如果phoneBook1可能是过时的,而phoneBook2更新一些但不够完整,那么正

确的用法可能是phoneBooke1.update(phoneBook2)。

如果认为两个电话本不

应该有重复的键时,使用newBook=mergeWithoutOverlap(phoneBook1,

phoneBook2)可以让你知道假设是否有错。

最后一种,如果一个是家里的电话本

而另一个是办公室的电话本,那么只要是后续的引用newBook['emily']的代码

能够处理newBook['emily']是元组('556-0091','667-1234')这一事实,就可

以用:

newBook=mergeWithoutOverlap(phoneBook1,phoneBook2)

拷贝:

copy模块

回到拷贝主题上来:

[:

]和.copy()技巧适用于90%的情况。

如果你正按照Python

的精神,编写可以处理任何参数类型的函数,有时需要拷贝X而不管X是什么。

这时就需要copy模块。

它提供了两个函数,copy和deepcopy。

第一个就像序列

的分片操作[:

]或是字典的copy方法。

第二个函数更微妙并且与深度嵌套结构有

关(这正是deepcopy的意思)。

例如用分片操作[:

]完整地拷贝listOne。

这个

技术产生了新的列表,如果原来的列表中的内容是不变的对象,如数字或字符串,

这个拷贝就是“真正的”拷贝。

然而假设listOne的第一项是一个字典(或任何

其他容易变化的对象),那么listOne的拷贝的第一项只是对同一个字典的新的

引用。

所以如果你修改了那个字典,显然listOne和它的拷贝都修改了。

用一个

例子可以看得更清楚些:

>>>importcopy

>>>listOne=[{"name":

"Willie","city":

"Providence,RI"},1,"tomato",3.0]

用Python完成常见的任务

275

>>>listTwo=listOne[:

]#orlistTwo=copy.copy(listOne)

>>>listThree=copy.deepcopy(listOne)

>>>listOne.append("kid")

>>>listOne[0]["city"]="SanFrancisco,CA"

>>>printlistOne,listTwo,listThree

[{'name':

'Willie','city':

'SanFrancisco,CA'},1,'tomato',3.0,'kid']

[{'name':

'Willie','city':

'SanFrancisco,CA'},1,'tomato',3.0]

[{'name':

'Willie','city':

'Providence,RI'},1,'tomato',3.0]

正如你所见,直接修改listOne仅仅修改了listOne。

对listOne的第一项的修

改影响到listTwo,但没有影响listThree。

这就是浅度拷贝([:

])和深度拷

贝的区别。

copy模块的函数知道如何拷贝可以拷贝的内置函数(注1),包括类和

实例。

排序

在第二章你知道列表有一个排序方法,有时你想要遍历整个排好序的列表而不想

影响列表的内容。

或者你也许想列出一个排好序的元组,而元组是不可变的,不

允许有排序的方法。

唯一的解决办法是拷贝一个列表,然后派序列表。

例如:

listCopy=list(myTuple)

listCopy.sort()

foriteminlistCopy:

printitem

#或者做别的任何事情

这也是处理那些没有内在顺序的数据结构的办法,例如字典。

字典非常快的一个

原因是实现时保留了改变键的顺序的权利。

这其实不是一个问题,因为你可以拷

贝字典的键然后遍历它:

keys=myDict.keys()

#返回字典的未排序的键

keys.sort()

forkeyinkeys:

#答应以键为序的健值对

printkey,myDict[key]

列表的sort方法使用的是Python的标准比较方案。

但有时你需要别的方案。

注1:

有些对象是不可拷贝的,如模块、文件对象和套接字。

记住,文件对象与磁盘上的文

件是不同的。

第九章

276

如当你对一个单词列表排序时,大小写也许是没有意义的。

而对字符串的标准比

较中,所有大写字母都在小写之前,所以'Baby'小于'apple'而'baby'大于

'apple'。

为了进行大小写无关排序,你需要定义一个有两个参数的函数,并且根

据第一个参数是小于,等于或大于第二个参数,分别返回-1,0,1。

所以你可以

这样写:

>>>defcaseIndependentSort(something,other):

...something,other=string.lower(something),string.lower(other)

...returncmp(something,other)

...

>>>testList=['this','is','A','sorted','List']

>>>testList.sort()

>>>printtestList

['A','List','is','sorted','this']

>>>testList.sort(caseIndependentSort)

>>>printtestList

['A','is','List','sorted','this']

我们使用内置的函数cmp来完成比较工作。

我们的排序函数只是把两项变成小写

字母然后排序。

也请注意小写转换是在比较函数局部范围内的,所以列表中的元

素并没有因排序而修改。

随机:

random模块

怎样随机排列一个序列呢?

比如一个文本行的列表。

最简单的办法是使用random

模块里的choice函数,它随机地返回序列的元素作为它的参数(注2)。

为了避

免重复地得到同样的行,记住删除已经选择了的项。

当操作一个列表对象时,使

用remove方法:

whilemyList:

#当myList空时停止循环

element=random.choice(myList)

myList.remove(element)

printelement,

如果你需要随机处理一个非列表对象,通常最简单的办法是把它转换为一个列表,

注2:

random模块提供了很多有用的函数,例如random函数,它返回一个介于0和1之间

的随机浮点数。

用Python完成常见的任务

277

然后对这个列表作随机处理,而不是对每种数据类型都采用新的办法。

这似乎是

一个浪费的办法,也许要产生一个很巨大的列表。

然而一般来说,对你似乎很大

的数据,对于计算机来说可能不那么大,感谢Python的引用系统。

而且不用对每

种数据类型采用不同的方法,所节约的时间是很多的。

Python的设计初衷就是要

节约时间;如果那意味着运行一个稍慢一点或者大一点的程序,那就让它这样吧。

如果你正在处理大量的数据,也许值得优化。

但只有当确实需要优化时才去优化,

否则将是浪费时间。

定义新的数据结构

对于数据结构来说,不要重复发明轮子这一原则尤其重要。

例如,Python的列表

和字典也许不是你习惯于使用的,但如果这些数据结构可以满足要求,你应当避

免设计自己的数据结构,它们使用的算法已经在各种情形下测试过,并且快而稳

定。

但有时这些算法的接口对某个特别的任务不方便。

例如,计算机科学的教科书中经常用其他数据结构术语如队列、堆栈来描述算法。

为了使用这些算法,定义与这些数据结构有同样方法的数据结构也许是有意义的。

(比如堆栈的pop和push,或者队列的enqueue和dequeue)。

而且,重用内置的

列表类型来实现堆栈也是有意义的。

换句话说,你需要行为像堆栈但却是基于列

表的结构。

最简单的办法是用一个类来包裹一个列表。

为了最低限度地实现一个

类,你可以这样写:

classStack:

def__init__(self,data):

self._data=list(data)

defpush(self,item):

self._data.append(item)

defpop(self):

item=self._data[-1]

delself._data[-1]

returnitem

下面的语句不仅易写,易懂,而且易读,易用。

>>>thingsToDo=Stack(['writetomom','invitefriendover','washthe

kid'])

第九章

278

>>>thingsToDo.push('dothedishes')

>>>printthingsToDo.pop()

dothedishes

>>>printthingsToDo.pop()

washthekid

在上面的堆栈类中用了两个标准的Python命名习惯,第一个是类名由大写字母开

始,以便与函数名区别开。

另一个是_data属性以一个下划线开始,这介于公共

属性(不以下划线开始)和私有属性之间(以两个下划线开始,参见第六章“类”)。

而Python的保留字在开始和结尾都有两个下划线。

这里的意思是:

_data是一个

属性,用户不应该直接访问,类的设计者希望这个“伪私有”属性只被类和子类

的方法使用。

定义新的列表和字典:

UserList和UserDict模块

前面展示的堆栈类作了恰当的工作。

它采取了关于堆栈的最小定义,只支持两个

操作:

push和pop。

然而,很快你就发现列表的特性确实好,比如可以用

for...in...的方式访问所有的成员。

这可以通过重用已有的代码来实现。

在这

里你应当应用UserList模块里定义的类UserList作为基类,堆栈由此派生而来。

库里也包括UserDict模块,它是一个封装字典的类。

一般来说,它们是用于特

别子类的基类。

#从UserList模块中导入UserList类

fromUserListimportUserList

#继承UserList类

classStack(UserList):

push=UserList.append

defpop(self):

item=self[-1]#使用__getitem__

delself[-1]

returnitem

这个堆栈是UserList的一个子类。

UserList类通过定义特别的__getitem__

和__delitem__方法实现了方括号的运算,所以前面代码里的pop能工作。

不必定义你自己的__init__方法,因为UserList已经定义了一个相当不错的。

最后只是说明push方法等于UserList的append方法。

现在我们可以用列表和

堆栈两种方式来操作了。

用Python完成常见的任务

279

>>>thingsToDo=Stack(['writetomom','invitefriendover','washthe

kid'])

>>>printthingsToDo#从UserList继承

['writetomom','invitefriendover','washthekid']

>>>thingsToDo.pop()

'washthekid'

>>>thingsToDo.push('changetheoil')

>>>forchoreinthingsToDo:

#我们也可以用"for..in.."遍历内容

...printchore#因为有__getitem__

...

writetomom

invitefriendover

changetheoil

注意:

当我们写这本书时,GuidovanRossom宣布在Python1.5.2(以及后续版本里),列表

对象将增加一个pop方法,它也有一个可选参数来指定pop的索引(缺省是列表最后的

一个成员)。

文件操作

脚本语言的设计目标之一是帮助人们快速而简单地做重复工作。

Web管理员,系

统管理员和程序员的经常需要做的一件事是:

从一个文件集合中选出一个子集,

对这个子集做某种操作,并把结果写到一个或一组输出文件中(例如,在某个目

录里的每个文件里,隔行查找以非#字符开头的行的最后一个词,并把它与文件

名一起打印出来)。

人们为这类任务已经设计了特定的工具,例如sed和awk。

们发现Python能很简单地完成这个工作。

操作一个文本文件里的每一行

当解析一个包含文本的输入文件时,sys模块是非常有用的。

在它的属性中有三

个文件对象,分别称为sys.stdin、sys.stdout和sys.stderr。

名字来源于三个

流的概念:

分别为标准输入、标准输出和标准错误。

它们与命令行工具有关,

print语句使用标准输出。

它是一个文件对象,具有以写模式打开的文件对象的

所有输出方法,如write和writelines。

另一个常用的流是标准输入(stdin),

它也是一个文件对象,不过它拥有的是输入方法,例如read、readline和

readlines。

下面的脚本会算出通过“管道”输入的文件行数:

第九章

280

importsys

data=sys.stdin.readlines()

print"Counted",len(data),"lines."

在Unix上你可以做如下的测试:

%catcountlines.py|pythoncountlines.py

Counted3lines.

在Windows或DOS上,你可以:

C:

\>typecountlines.py|pythoncountlines.py

Counted3lines.

当实现简单的过滤操作时,readlines函数是有用的。

这里有一些过滤操作的例

子:

寻找所有以#开始的行

importsys

forlineinsys.stdin.readlines():

ifline[0]=='#':

printline,

注意print语句后的逗号是需要的,因为line字符串里已经有一个换行符。

取出一个文件的第四列(这里列是由空白符定义的)

importsys,string

forlineinsys.stdin.readlines():

words=string.split(line)

iflen(words)>=4:

printwords[3]

我们通过words列表的长度判断是否确实至少有四个列,最后两行可以用

try/except惯用法代替,这在Python里是常见的:

try:

printwords[3]

exceptIndexError:

#没有足够的列

pass

取出文件的第四列,列由冒号分开,并用小写输出

importsys,string

forlineinsys.stdin.readlines():

words=string.split(line,':

')

用Python完成常见的任务

281

iflen(words)>=4:

printstring.lower(words[3])

打印头10行,最后10行,并隔行打印输出

importsys,string

lines=sys.stdin.readlines()

sys.stdout.writelines(lines[:

10])

#头10行

sys.stdout.writelines(lines[-10:

])

#最后10行

forlineIndexinrange(0,len(lines),2):

#0,2,4,......

sys.stdout.write(lines[lineIndex])

#0,2,4,......行

计算单词“Python”在一个文件里出现的次数

importstring

text=open(fname).read()

printstring.count(text,'Python')

把一个文件的列变换为一个列表的行

在这个比较复杂的例子里,任务是“转置”一个文件,设想你有这样一个文

件:

Name:

WillieMarkGuidoMaryRachelAhmed

Level:

543164

Tag#:

123444515515512418815132

而你希望它变成这样:

Name:

Level:

Tag#:

Willie51234

Mark44451

...

你可以用下面的代码:

importsys,string

lines=sys.stdin.readlines()

wordlists=[]

forlineinlines:

words=string.split(line)

wordlists.append(words)

forrowinrange(len(wordlists[0])):

forcolinrange(len(wordlists)):

printwordlists[col][row]+'\t',

print

当然你应当用更多的防卫性编程技巧来处理一些可能的情况,比如也许不是

所有的行都有相同的单词数,也许丢失了数据等等。

这些就作为练习留给读

者。

第九章

282

选择数据块的大小

前面的所有例子都假设你能一次读入整个文件。

然而有时候这是不可能的,比如

在内存较小的计算机上处理大文件,或者处理不断地增加的文件(如日志文件)。

对这种情况你可以用一个while/readline组合,一次读入文件的一小部分直到

读完。

对于不是基于行的文本文件,你必须一次读入一个字符:

#逐字地读入

while1:

next=sys.stdin.read

(1)

#读入一个单字符串

ifnotnext:

#或者读到EOF时是空串

break

处理字符串'next'

注意文件对象的read()方法在文件结尾时返回一个空串,并由此而跳出循环。

而更常见的是你将处理基于行的文本文件,并且一次处理一行:

#逐行地读入

while1:

next=sys.stdin.readline()

#读入一个单行字符串

ifnotnext:

#或者读到EOF时是空串

break

处理行'next'

处理命令行上指定的一组文件

能够读stdin是一个

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1