diff --git a/build/Python.pdf b/build/Python.pdf new file mode 100644 index 0000000..3435e49 Binary files /dev/null and b/build/Python.pdf differ diff --git a/build/python_readlists.epub b/build/python_readlists.epub new file mode 100644 index 0000000..f259698 Binary files /dev/null and b/build/python_readlists.epub differ diff --git a/content/07.md b/content/07.md index 577eb65..0d42d2e 100644 --- a/content/07.md +++ b/content/07.md @@ -1,15 +1,9 @@ #Python基础07 函数 - - - - 函数最重要的目的是方便我们重复使用相同的一段程序。 将一些操作隶属于一个函数,以后你想实现相同的操作的时候,只用调用函数名就可以,而不需要重复敲所有的语句。 - - ##函数的定义 首先,我们要定义一个函数, 以说明这个函数的功能。 @@ -26,7 +20,6 @@ def square_sum(a,b): 我们已经在循环和选择中见过冒号和缩进来表示的隶属关系。 - c = a**2 + b**2 # 这一句是函数内部进行的运算 return c # 返回c的值,也就是输出的功能。Python的函数允许不返回值,也就是不用return。 diff --git a/content/08.md b/content/08.md index 9b764db..1f69024 100644 --- a/content/08.md +++ b/content/08.md @@ -1,24 +1,17 @@ #Python基础08 面向对象的基本概念 - - - Python使用类(class)和对象(object),进行面向对象(object-oriented programming,简称OOP)的编程。 面向对象的最主要目的是提高程序的重复使用性。我们这么早切入面向对象编程的原因是,Python的整个概念是基于对象的。了解OOP是进一步学习Python的关键。 下面是对面向对象的一种理解,基于分类。 - - ##相近对象,归为类 在人类认知中,会根据属性相近把东西归类,并且给类别命名。比如说,鸟类的共同属性是有羽毛,通过产卵生育后代。任何一只特别的鸟都在鸟类的原型基础上的。 面向对象就是模拟了以上人类认知过程。在Python语言,为了听起来酷,我们把上面说的“东西”称为对象(object)。 - - 先定义鸟类 ```python class Bird(object): @@ -27,8 +20,6 @@ class Bird(object): ``` 我们定义了一个类别(class),就是鸟(Bird)。在隶属于这个类比的语句块中,我们定义了两个变量,一个是有羽毛(have_feather),一个是生殖方式(way_of_reproduction),这两个变量对应我们刚才说的属性(attribute)。我们暂时先不说明括号以及其中的内容,记为问题1。 - - 假设我养了一只小鸡,叫summer。它是个对象,且属于鸟类。使用前面定义的类: ```python summer = Bird() @@ -36,12 +27,8 @@ print summer.way_of_reproduction ``` 通过第一句创建对象,并说明summer是类别鸟中的一个对象,summer就有了鸟的类属性,对属性的引用是通过 对象.属性(object.attribute) 的形式实现的。 - - 可怜的summer,你就是个有毛产的蛋货,好不精致。 - - ##动作 日常认知中,我们在通过属性识别类别的时候,有时根据这个东西能做什么事情来区分类别。比如说,鸟会移动。这样,鸟就和房屋的类别区分开了。这些动作会带来一定的结果,比如移动导致位置的变化。 @@ -65,24 +52,16 @@ print 'after move:',summer.move(5,8) (它的参数中有一个self,它是为了方便我们引用对象自身。方法的第一个参数必须是self,无论是否用到。有关self的内容会在下一讲展开) - - 另外两个参数,dx, dy表示在x、y两个方向移动的距离。move方法会最终返回运算过的position。 在最后调用move方法的时候,我们只传递了dx和dy两个参数,不需要传递self参数(因为self只是为了内部使用)。 - - 我的summer可以跑了。 - - ##子类 类别本身还可以进一步细分成子类 - - 比如说,鸟类可以进一步分成鸡,大雁,黄鹂。 在OOP中,我们通过继承(inheritance)来表达上述概念。 @@ -130,8 +109,6 @@ print summer.move(5,8) self代表了根据类定义而创建的对象。 - - 建立对一个对象: 对象名 = 类名() 引用对象的属性: object.attribute diff --git a/content/09.md b/content/09.md index a30029e..1b36a88 100644 --- a/content/09.md +++ b/content/09.md @@ -1,12 +1,7 @@ #Python基础09 面向对象的进一步拓展 - - - 我们熟悉了对象和类的基本概念。我们将进一步拓展,以便能实际运用对象和类。 - - ##调用类的其它信息 上一讲中提到,在定义方法时,必须有self这一参数。这个参数表示某个对象。对象拥有类的所有性质,那么我们可以通过self,调用类属性。 @@ -25,16 +20,10 @@ li_lei.laugh_100th() ``` 这里有一个类属性laugh。在方法show_laugh()中,通过self.laugh,调用了该属性的值。 - - 还可以用相同的方式调用其它方法。方法show_laugh(),在方法laugh_100th中()被调用。 - - 通过对象可以修改类属性值。但这是危险的。类属性被所有同一类及其子类的对象共享。类属性值的改变会影响所有的对象。 - - ##__init__()方法 __init__()是一个特殊方法(special method)。Python有一些特殊方法。Python会特殊的对待它们。特殊方法的特点是名字前后有两个下划线。 @@ -52,12 +41,12 @@ summer = happyBird('Happy,Happy!') 屏幕上打印: +> We are happy birds.Happy,Happy! -We are happy birds.Happy,Happy! 我们看到,尽管我们只是创建了summer对象,但__init__()方法被自动调用了。最后一行的语句(summer = happyBird...)先创建了对象,然后执行: - +```python summer.__init__(more_words) - +``` 'Happy,Happy!' 被传递给了__init__()的参数more_words @@ -68,8 +57,6 @@ summer.__init__(more_words) 在一些情况下,我们定义对象的性质,用于记录该对象的特别信息。比如说,人这个类。性别是某个人的一个性质,不是所有的人类都是男,或者都是女。这个性质的值随着对象的不同而不同。李雷是人类的一个对象,性别是男;韩美美也是人类的一个对象,性别是女。 - - 当定义类的方法时,必须要传递一个self的参数。这个参数指代的就是类的一个对象。我们可以通过操纵self,来修改某个对象的性质。比如用类来新建一个对象,即下面例子中的li_lei, 那么li_lei就被self表示。我们通过赋值给self.attribute,给li_lei这一对象增加一些性质,比如说性别的男女。self会传递给各个方法。在方法内部,可以通过引用self.attribute,查询或修改对象的性质。 这样,在类属性的之外,又给每个对象增添了各自特色的性质,从而能描述多样的世界。 @@ -89,12 +76,8 @@ li_lei.printGender() li_lei拥有了对象性质gender。gender不是一个类属性。Python在建立了li_lei这一对象之后,使用li_lei.gender这一对象性质,专门储存属于对象li_lei的特有信息。 - - 对象的性质也可以被其它方法调用,调用方法与类属性的调用相似,正如在printGender()方法中的调用。 - - ##总结 通过self调用类属性 diff --git a/content/10.md b/content/10.md index 3f67d04..569daf1 100644 --- a/content/10.md +++ b/content/10.md @@ -1,8 +1,5 @@ #Python基础10 反过头来看看 - - - 从最初的“Hello World”,走到面向对象。该回过头来看看,教程中是否遗漏了什么。 我们之前提到一句话,"Everything is Object". 那么我们就深入体验一下这句话。 @@ -13,41 +10,38 @@ dir()用来查询一个类或者对象所有属性。你可以尝试一下 +```python >>>print dir(list) +``` help()用来查询的说明文档。你可以尝试一下 +```python >>>print help(list) +``` (list是Python内置的一个类,对应于我们之前讲解过的列表) - ##list是一个类 在上面以及看到,表是Python已经定义好的一个类。当我们新建一个表时,比如: +```python >>>nl = [1,2,5,3,5] +``` 实际上,nl是类list的一个对象。 - - 实验一些list的方法: ```python >>>print nl.count(5) # 计数,看总共有多少个5 - >>>print nl.index(3) # 查询 nl 的第一个3的下标 - >>>nl.append(6) # 在 nl 的最后增添一个新元素6 - >>>nl.sort() # 对nl的元素排序 - >>>print nl.pop() # 从nl中去除最后一个元素,并将该元素返回。 - >>>nl.remove(2) # 从nl中去除第一个2 - >>>nl.insert(0,9) # 在下标为0的位置插入9 ``` 总之,list是一个类。每个列表都属于该类。 @@ -62,8 +56,9 @@ Python补充中有list常用方法的附录。 这个方法定义了"+"运算符对于list对象的意义,两个list的对象相加时,会进行的操作。 +```python >>>print [1,2,3] + [5,6,9] - +``` 运算符,比如+, -, >, <, 以及下标引用[start:end]等等,从根本上都是定义在类内部的方法。 @@ -71,8 +66,9 @@ Python补充中有list常用方法的附录。 尝试一下 - +```python >>>print [1,2,3] - [3,4] +``` 会有错误信息,说明该运算符“-”没有定义。现在我们继承list类,添加对"-"的定义 @@ -93,20 +89,14 @@ print superList([1,2,3]) - superList([3,4]) (教程最后也会给出一个特殊方法的清单) - - 定义运算符对于复杂的对象非常有用。举例来说,人类有多个属性,比如姓名,年龄和身高。我们可以把人类的比较(>, <, =)定义成只看年龄。这样就可以根据自己的目的,将原本不存在的运算增加在对象上了。 - - ##下一步 希望你已经对Python有了一个基本了解。你可能跃跃欲试,要写一些程序练习一下。这会对你很有好处。 但是,Python的强大很大一部分原因在于,它提供有很多已经写好的,可以现成用的对象。我们已经看到了内置的比如说list,还有tuple等等。它们用起来很方便。在Python的标准库里,还有大量可以用于操作系统互动,Internet开发,多线程,文本处理的对象。而在所有的这些的这些的基础上,又有很多外部的库包,定义了更丰富的对象,比如numpy, tkinter, django等用于科学计算,GUI开发,web开发的库,定义了各种各样的对象。对于一般用户来说,使用这些库,要比自己去从头开始容易得多。我们要开始攀登巨人的肩膀了。 - - 谢谢你的关注, 欢迎来到Python的世界。 diff --git "a/content/Python\345\260\217\351\242\230\347\233\256 \351\222\210\345\257\271\345\277\253\351\200\237\346\225\231\347\250\213.md" "b/content/Python\345\260\217\351\242\230\347\233\256 \351\222\210\345\257\271\345\277\253\351\200\237\346\225\231\347\250\213.md" new file mode 100644 index 0000000..a2dfd36 --- /dev/null +++ "b/content/Python\345\260\217\351\242\230\347\233\256 \351\222\210\345\257\271\345\277\253\351\200\237\346\225\231\347\250\213.md" @@ -0,0 +1,48 @@ +Python小题目 针对快速教程 + + + + +作业的目的是帮助熟悉之前学习的内容: + + + + 1. + +写一个程序,判断2008年是否是闰年。 + +写一个程序,用于计算2008年10月1日是这一年的第几天?(2008年1月1日是这一年的第一天) + +这些小题目是为了方便大家加深对Python理解而设计的。 + + + +2. + +有一个record.txt的文档,内容如下: +```python +# name, age, score + +tom, 12, 86 + +Lee, 15, 99 + +Lucy, 11, 58 + +Joseph, 19, 56 +``` +第一栏为姓名(name),第二栏为年纪(age),第三栏为得分(score) + +现在,写一个Python程序, + +1)读取文件 + +2)打印如下结果: + +得分低于60的人都有谁? + +谁的名字以L开头? + +所有人的总分是多少? + +3)姓名的首字母需要大写,该record.txt是否符合此要求? 如何纠正错误的地方? diff --git "a/content/Python\345\277\253\351\200\237\346\225\231\347\250\213\346\200\273\347\273\223.md" "b/content/Python\345\277\253\351\200\237\346\225\231\347\250\213\346\200\273\347\273\223.md" new file mode 100644 index 0000000..1ba7308 --- /dev/null +++ "b/content/Python\345\277\253\351\200\237\346\225\231\347\250\213\346\200\273\347\273\223.md" @@ -0,0 +1,21 @@ +Python快速教程总结 + + + + + +到现在为止,Python快速教程就到一段落。谢谢大家的鼓励和支持,特别是对其中错误的包容和指正。 + + + +在第一部分,基础部分中,除了一些基础知识,我们着力于建立对象的概念。在第二部分中,我们深入到许多实用并且常用的Python用法。如果只是想了解Python是什么,那么第一部分应该可以给出一个基本概念。加上第二部分的话,我们应该可以有足够的知识读懂Python程序,并编写一些应用。 + +我会在以后写一些选读性的随笔,以便对教程更进一步补充。 + + + +我还在考虑下一步在博客中写什么。Python依然有一个庞大的标准库可以展开。但标准库的讲解离不开对计算机原理和互联网原理的了解,很难以一个快速教程的方式呈现出来。但离开标准库,Python也显得很单薄。一个可能是,先写一个总体纲要,描述一下标准库各个包所包含的内容,以及可能的用途,不知是否可行? + +另外,最近看了一些Google Engine和Django的内容,觉得也不错。只可惜自己在这方面经验太浅,不知从何说起。 + +另一方面,我个人的Python经验也非常有限。希望能多参与到一些项目,以便能积累经验。 diff --git "a/content/Python\346\240\207\345\207\206\345\272\223\342\200\224\342\200\224\350\265\260\351\251\254\350\247\202\350\212\261.md" "b/content/Python\346\240\207\345\207\206\345\272\223\342\200\224\342\200\224\350\265\260\351\251\254\350\247\202\350\212\261.md" new file mode 100644 index 0000000..3d548f4 --- /dev/null +++ "b/content/Python\346\240\207\345\207\206\345\272\223\342\200\224\342\200\224\350\265\260\351\251\254\350\247\202\350\212\261.md" @@ -0,0 +1,119 @@ +#Python标准库——走马观花 + + + + + +Python的一大好处在于它有一套很有用的标准库(standard library)。标准库是随着Python一起安装在你的电脑中的,是Python的一部分 (当然也有特殊情况。有些场合会因为系统安全性的要求,不使用全部的标准库,比如说Google App Engine)。 + + + +利用已有的类(class)和函数(function)进行开发,可以省去你从头写所有程序的苦恼。这些标准库就是盖房子已经烧好的砖,要比你自己去烧砖来得便捷得多。 + + + +我将根据我个人的使用经验中,先挑选出标准库下面三个方面的包(package)介绍,以说明标准库的强大功能: + +Python增强 + +系统互动 + +网络 + + + +##第一类:Python增强 + +Python自身的已有的一些功能可以随着标准库的使用而得到增强。 + +1) 文字处理 + +Python的string类提供了对字符串进行处理的方法。但Python并不止步于此。通过标准库中的re包,Python实现了对正则表达式(regular expression)的支持。Python的正则表达式可以和Perl以及Linux bash的正则表达相媲美。 + +(正则表达式通过自定义的模板在文本中搜索或替换符合该模板的字符串。比如你可以搜索一个文本中所有的数字。正则表达式的关键在于根据自己的需要构成模板。) + +此外,Python标准库还为字符串的输出提供更加丰富的格式, 比如: string包,textwrap包。 + + + +2) 数据对象 + +我们之前的快速教程介绍了表(list), 字典(dictionary)等数据对象。它们各自有不同的特征,适用于不同场合的对数据的组织和管理。Python的标准库定义了更多的数据对象,比如说数组(array),队列(Queue)。这些数据对象也分别有各自的特点和功能。一个熟悉数据结构(data structure)的Python用户可以在这些包中找到自己需要的数据结构。 + +此外,我们也会经常使用copy包,以复制对象。 + + + +3) 日期和时间 + +日期和时间的管理并不复杂,但容易犯错。Python的标准库中对日期和时间的管理颇为完善(利用time包管理时间,利用datetime包管理日期和时间),你不仅可以进行日期时间的查询和变换(比如:2012年7月18日对应的是星期几),还可以对日期时间进行运算(比如2000.1.1 13:00的378小时之后是什么日期,什么时间)。通过这些标准库,还可以根据需要控制日期时间输出的文本格式(比如:输出’2012-7-18‘还是'18 Jul 2012') + + + +4) 数学运算 + +标准库中,Python定义了一些新的数字类型(decimal包, fractions包), 以弥补之前的数字类型(integer, float)可能的不足。标准库还包含了random包,用于处理随机数相关的功能(产生随机数,随机取样等)。math包补充了一些重要的数学常数和数学函数,比如pi,三角函数等等。 + +(尽管numpy并不是标准库中的包,但它的数组运算的良好支持,让它在基于Python的科研和计算方面得到相当广泛的应用,可以适当关注。) + + + +5) 存储 + +之前我们的快速教程中,只提及了文本的输入和输出。实际上,Python可以输入或输出任意的对象。这些对象可以通过标准库中的pickle包转换成为二进制格式(binary),然后存储于文件之中,也可以反向从二进制文件中读取对象。 + +此外,标准库中还支持基本的数据库功能(sqlite3包)。XML和csv格式的文件也有相应的处理包。 + + + +##第二类:系统互动 + +系统互动,主要指Python和操作系统(operate system)、文件系统(file system)的互动。Python可以实现一个操作系统的许多功能。它能够像bash脚本那样管理操作系统,这也是Python有时被成为脚本语言的原因。 + + + +1) Python运行控制 + +sys包被用于管理Python自身的运行环境。Python是一个解释器(interpreter), 也是一个运行在操作系统上的程序。我们可以用sys包来控制这一程序运行的许多参数,比如说Python运行所能占据的内存和CPU, Python所要扫描的路径等。另一个重要功能是和Python自己的命令行互动,从命令行读取命令和参数。 + + + +2) 操作系统 + +如果说Python构成了一个小的世界,那么操作系统就是包围这个小世界的大世界。Python与操作系统的互动可以让Python在自己的小世界里管理整个大世界。 + +os包是Python与操作系统的接口。我们可以用os包来实现操作系统的许多功能,比如管理系统进程,改变当前路径(相当于’cd‘),改变文件权限等,建立。但要注意,os包是建立在操作系统的平台上的,许多功能在Windows系统上是无法实现的。另外,在使用os包中,要注意其中的有些功能已经被其他的包取代。 + +我们通过文件系统来管理磁盘上储存的文件。查找、删除,复制文件,以及列出文件列表等都是常见的文件操作。这些功能经常可以在操作系统中看到(比如ls, mv, cp等Linux命令),但现在可以通过Python标准库中的glob包、shutil包、os.path包、以及os包的一些函数等,在Python内部实现。 + +subprocess包被用于执行外部命令,其功能相当于我们在操作系统的命令行中输入命令以执行,比如常见的系统命令'ls'或者'cd',还可以是任意可以在命令行中执行的程序。 + + + +4) 线程与进程 + +Python支持多线程(threading包)运行和多进程(multiprocessing包)运行。通过多线程和多进程,可以提高系统资源的利用率,提高计算机的处理速度。Python在这些包中,附带有相关的通信和内存管理工具。此外,Python还支持类似于UNIX的signal系统,以实现进程之间的粗糙的信号通信。 + + + +##第三类:网络 + +现在,网络功能的强弱很大程度上决定了一个语言的成功与否。从Ruby, JavaScript, php身上都可以感受到这一点。Python的标准库对互联网开发的支持并不充分,这也是Django等基于Python的项目的出发点: 增强Python在网络方面的应用功能。这些项目取得了很大的成功,也是许多人愿意来学习Python的一大原因。但应注意到,这些基于Python的项目也是建立在Python标准库的基础上的。 + + + +1) 基于socket层的网络应用 + +socket是网络可编程部分的底层。通过socket包,我们可以直接管理socket,比如说将socket赋予给某个端口(port),连接远程端口,以及通过连接传输数据。我们也可以利用SocketServer包更方便地建立服务器。 + +通过与多线程和多进程配合,建立多线程或者多进程的服务器,可以有效提高服务器的工作能力。此外,通过asyncore包实现异步处理,也是改善服务器性能的一个方案。 + + + +2) 互联网应用 + +在实际应用中,网络的很多底层细节(比如socket)都是被高层的协议隐藏起来的。建立在socket之上的http协议实际上更容易也更经常被使用。http通过request/responce的模式建立连接并进行通信,其信息内容也更容易理解。Python标准库中有http的服务器端和客户端的应用支持(BaseHTTPServer包; urllib包, urllib2包), 并且可以通过urlparse包对URL(URL实际上说明了网络资源所在的位置)进行理解和操作。 + + + +以上的介绍比较粗糙,只希望能为大家提供一个了解标准库的入口。欢迎大家一起分享标准库的使用经验。 diff --git "a/content/Python\346\240\207\345\207\206\345\272\223\347\232\204\345\255\246\344\271\240\345\207\206\345\244\207.md" "b/content/Python\346\240\207\345\207\206\345\272\223\347\232\204\345\255\246\344\271\240\345\207\206\345\244\207.md" new file mode 100644 index 0000000..be9d29c --- /dev/null +++ "b/content/Python\346\240\207\345\207\206\345\272\223\347\232\204\345\255\246\344\271\240\345\207\206\345\244\207.md" @@ -0,0 +1,84 @@ +#Python标准库的学习准备 + + + + + +Python标准库是Python强大的动力所在,我们已经在前文中有所介绍。由于标准库所涉及的应用很广,所以需要学习一定的背景知识。 + + + +##硬件原理 + +这一部份需要了解内存,CPU,磁盘存储以及IO的功能和性能,了解计算机工作的流程,了解指令的概念。这些内容基础而重要。 + +Python标准库的一部份是为了提高系统的性能(比如mmap),所以有必要了解基本的计算机各个组成部分的性能。 + + + +##操作系统 + +在了解操作系统时,下面是重点: + +1) 操作系统的进程管理,比如什么是UID, PID, daemon + +2) 进程之间的信号通信,比如使用kill传递信号的方式 + +学习进程相关的内容,是为了方便于学习os包,thread包,multiprocessing包,signal包 + +3) 文件管理,文件的几种类型。 + +4) 文件读写(IO)接口 + +5) 文件的权限以及其它的文件信息(meta data) + +6) 常用系统命令以及应用,比如ls, mv, rm, mkdir, chmod, zip, tar..., + +学习文件相关的内容,,是为了学习os包, shutil包中文件管理相关的部分。学习文件接口对于文本输入输出的理解很重要,也会影响到对于socket包, select包概念的理解。此外,python中的归档(archive)和压缩(compress)功能也和操作系统中的类似。 + +7) Linux shell,比如说file name matching,对于理解glob包等有帮助。如果你对Linux的正则表达(regular expression)有了解的话,python的正则表达的学习会变得比较容易。学习Linux命令行中的参数传递对于理解python标准库中解析命令行的包也是有用的。 + + + +##网络 + +Python的一大应用是在网络方面。但Python和标准库只是提供了接口,并不涉及底层。网络知识可以大大降低学习曲线的陡度。 + +1) TCP/IP的基础的分层架构。这方面的内容太广博了,所以可以有选择地了解骨干知识。 + +2) 常用的应用层协议,比如http, 以及邮件相关的协议,特别是它们的工作过程。 + +3) 根据需要,了解html/css/javascript/jQuery/frame等 + +如果想利用python建服务器,比如在google app engine上,这些知识是需要的。 + + + +##算法与数据结构 + +标准库中定义有一些数据对象的封装。因此,你并不需要重头编写它们。相关数据结构的应用需要一些数据结构的知识,比如队列,树等。 + +标准库中已经实现了许多算法,比如排序等,可以方便的调用。算法的基础知识可以帮助你做决定。 + + + +##数据库 + +Python中内置了sqlite3。如果你只需要一个简单的数据库,可以直接从标准库中调用sqlite3。 + +当使用Python中数据库相关的包时(比如sqlite3),需要对数据库,特别是关系型数据库,有一个基本了解。 + + + +##加密和文本编码 + +Python的加密算法同样基于一些经典加密算法,比如MD5,RSA算法。加密的基本知识将很有帮助。 + +使用非ASCII编码,比如中文时,文本编码的知识很重要。 + + + +总结 + +Python基本的对象概念和动态类型概念。可以参照快速教程,并尝试阅读更多的资料和源码,来加深对概念的理解。Python标准库学习 +的难度在于背景知识。一个了解相关背景知识(或者其它语言的库)的程序员,可以在很短的时间内掌握Python基础库。 diff --git "a/content/Python\350\241\245\345\205\20501 \345\272\217\345\210\227\347\232\204\346\226\271\346\263\225.md" "b/content/Python\350\241\245\345\205\20501 \345\272\217\345\210\227\347\232\204\346\226\271\346\263\225.md" new file mode 100644 index 0000000..d4df176 --- /dev/null +++ "b/content/Python\350\241\245\345\205\20501 \345\272\217\345\210\227\347\232\204\346\226\271\346\263\225.md" @@ -0,0 +1,123 @@ +#Python补充01 序列的方法 + + + +在快速教程中,我们了解了最基本的序列(sequence)。回忆一下,序列包含有定值表(tuple)和表(list)。此外,字符串(string)是一种特殊的定值表。表的元素可以更改,定值表一旦建立,其元素不可更改。 + + + +任何的序列都可以引用其中的元素(item)。 + + + +下面的内建函数(built-in function)可用于序列(表,定值表,字符串): + +# s为一个序列 + +len(s) 返回: 序列中包含元素的个数 + +min(s) 返回: 序列中最小的元素 + +max(s) 返回: 序列中最大的元素 + +all(s) 返回: True, 如果所有元素都为True的话 + +any(s) 返回: True, 如果任一元素为True的话 + + + +下面的方法主要起查询功能,不改变序列本身, 可用于表和定值表: + +sum(s) 返回:序列中所有元素的和 + +# x为元素值,i为下标(元素在序列中的位置) + +s.count(x) 返回: x在s中出现的次数 + +s.index(x) 返回: x在s中第一次出现的下标 + + + +由于定值表的元素不可变更,下面方法只适用于表: + +# l为一个表, l2为另一个表 + +l.extend(l2) 在表l的末尾添加表l2的所有元素 + +l.append(x) 在l的末尾附加x元素 + +l.sort() 对l中的元素排序 + +l.reverse() 将l中的元素逆序 + +l.pop() 返回:表l的最后一个元素,并在表l中删除该元素 + +del l[i] 删除该元素 + +(以上这些方法都是在原来的表的上进行操作,会对原来的表产生影响,而不是返回一个新表。) + + + +下面是一些用于字符串的方法。尽管字符串是定值表的特殊的一种,但字符串(string)类有一些方法是改变字符串的。这些方法的本质不是对原有字符串进行操作,而是删除原有字符串,再建立一个新的字符串,所以并不与定值表的特点相矛盾。 + +#str为一个字符串,sub为str的一个子字符串。s为一个序列,它的元素都是字符串。width为一个整数,用于说明新生成字符串的宽度。 + +str.count(sub) 返回:sub在str中出现的次数 + +str.find(sub) 返回:从左开始,查找sub在str中第一次出现的位置。如果str中不包含sub,返回 -1 + +str.index(sub) 返回:从左开始,查找sub在str中第一次出现的位置。如果str中不包含sub,举出错误 + +str.rfind(sub) 返回:从右开始,查找sub在str中第一次出现的位置。如果str中不包含sub,返回 -1 + +str.rindex(sub) 返回:从右开始,查找sub在str中第一次出现的位置。如果str中不包含sub,举出错误 + + + +str.isalnum() 返回:True, 如果所有的字符都是字母或数字 + +str.isalpha() 返回:True,如果所有的字符都是字母 + +str.isdigit() 返回:True,如果所有的字符都是数字 + +str.istitle() 返回:True,如果所有的词的首字母都是大写 + +str.isspace() 返回:True,如果所有的字符都是空格 + +str.islower() 返回:True,如果所有的字符都是小写字母 + +str.isupper() 返回:True,如果所有的字符都是大写字母 + + + +str.split([sep, [max]]) 返回:从左开始,以空格为分割符(separator),将str分割为多个子字符串,总共分割max次。将所得的子字符串放在一个表中返回。可以str.split(',')的方式使用逗号或者其它分割符 + +str.rsplit([sep, [max]]) 返回:从右开始,以空格为分割符(separator),将str分割为多个子字符串,总共分割max次。将所得的子字符串放在一个表中返回。可以str.rsplit(',')的方式使用逗号或者其它分割符 + + + +str.join(s) 返回:将s中的元素,以str为分割符,合并成为一个字符串。 + +str.strip([sub]) 返回:去掉字符串开头和结尾的空格。也可以提供参数sub,去掉位于字符串开头和结尾的sub + +str.replace(sub, new_sub) 返回:用一个新的字符串new_sub替换str中的sub + + + +str.capitalize() 返回:将str第一个字母大写 + +str.lower() 返回:将str全部字母改为小写 + +str.upper() 返回:将str全部字母改为大写 + +str.swapcase() 返回:将str大写字母改为小写,小写改为大写 + +str.title() 返回:将str的每个词(以空格分隔)的首字母大写 + + + +str.center(width) 返回:长度为width的字符串,将原字符串放入该字符串中心,其它空余位置为空格。 + +str.ljust(width) 返回:长度为width的字符串,将原字符串左对齐放入该字符串,其它空余位置为空格。 + +str.rjust(width) 返回:长度为width的字符串,将原字符串右对齐放入该字符串,其它空余位置为空格。 diff --git a/content/STL01.md b/content/STL01.md new file mode 100644 index 0000000..67fb06f --- /dev/null +++ b/content/STL01.md @@ -0,0 +1,180 @@ +#Python标准库01 正则表达式 (re包) + + + + +我将从正则表达式开始讲Python的标准库。正则表达式是文字处理中常用的工具,而且不需要额外的系统知识或经验。我们会把系统相关的包放在后面讲解。 + + + +正则表达式(regular expression)主要功能是从字符串(string)中通过特定的模式(pattern),搜索想要找到的内容。 + + +##语法 + +之前,我们简介了字符串相关的处理函数。我们可以通过这些函数实现简单的搜索功能,比如说从字符串“I love you”中搜索是否有“you”这一子字符串。但有些时候,我们只是模糊地知道我们想要找什么,而不能具体说出我是在找“you”,比如说,我想找出字符串中包含的数字,这些数字可以是0到9中的任何一个。这些模糊的目标可以作为信息写入正则表达式,传递给Python,从而让Python知道我们想要找的是什么。 + +(官方documentation) + +在Python中使用正则表达式需要标准库中的一个包re。 +```python +import re +m = re.search('[0-9]','abcd4ef') +print(m.group(0)) +``` +re.search()接收两个参数,第一个'[0-9]'就是我们所说的正则表达式,它告诉Python的是,“听着,我从字符串想要找的是从0到9的一个数字字符”。 + +re.search()如果从第二个参数找到符合要求的子字符串,就返回一个对象m,你可以通过m.group()的方法查看搜索到的结果。如果没有找到符合要求的字符,re.search()会返回None。 + + + +如果你熟悉Linux或者Perl, 你应该已经熟悉正则表达式。当我们打开Linux shell的时候,可以用正则表达式去查找或着删除我们想要的文件,比如说: +```bash +$rm book[0-9][0-9].txt +``` +这就是要删除类似于book02.txt的文件。book[0-9][0-9].txt所包含的信息是,以book开头,后面跟两个数字字符,之后跟有".txt"的文件名。如果不符合条件的文件名,比如说: + +bo12.txt + +book1.txt + +book99.text + +都不会被选中。 + +Perl中内建有正则表达式的功能,据说是所有正则表达式系统中最强的,这也是Perl成为系统管理员利器的一个原因。 + + + +##正则表达式的函数 +```python +m = re.search(pattern, string) # 搜索整个字符串,直到发现符合的子字符串。 +m = re.match(pattern, string) # 从头开始检查字符串是否符合正则表达式。必须从字符串的第一个字符开始就相符。 +``` +可以从这两个函数中选择一个进行搜索。上面的例子中,我们如果使用re.match()的话,则会得到None,因为字符串的起始为‘a’, 不符合'[0-9]'的要求。 + +对于返回的m, 我们使用m.group()来调用结果。(我们会在后面更详细解释m.group()) + + + +我们还可以在搜索之后将搜索到的子字符串进行替换: + +str = re.sub(pattern, replacement, string) # 在string中利用正则变换pattern进行搜索,对于搜索到的字符串,用另一字符串replacement替换。返回替换后的字符串。 + + +此外,常用的正则表达式函数还有 + +re.split() # 根据正则表达式分割字符串, 将分割后的所有子字符串放在一个表(list)中返回 + +re.findall() # 根据正则表达式搜索字符串,将所有符合的子字符串放在一给表(list)中返回 + + + +(在熟悉了上面的函数后,可以看一下re.compile(),以便于提高搜索效率。) + + + +##写一个正则表达式 + +关键在于将信息写成一个正则表达式。我们先看正则表达式的常用语法: + +1)单个字符: + +. 任意的一个字符 + +a|b 字符a或字符b + +[afg] a或者f或者g的一个字符 + +[0-4] 0-4范围内的一个字符 + +[a-f] a-f范围内的一个字符 + +[^m] 不是m的一个字符 + +\s 一个空格 + +\S 一个非空格 + +\d [0-9] + +\D [^0-9] + +\w [0-9a-zA-Z] + +\W [^0-9a-zA-Z] + + + +2)重复 + +紧跟在单个字符之后,表示多个这样类似的字符 + +* 重复 >=0 次 + ++ 重复 >=1 次 + +? 重复 0或者1 次 + +{m} 重复m次。比如说 a{4}相当于aaaa,再比如说[1-3]{2}相当于[1-3][1-3] + +{m, n} 重复m到n次。比如说a{2, 5}表示a重复2到5次。小于m次的重复,或者大于n次的重复都不符合条件。 + + + +正则表达 相符的字符串举例 + +[0-9]{3,5} 9678 + +a?b b + +a+b aaaaab + + + +3) 位置 + +^ 字符串的起始位置 + +$ 字符串的结尾位置 + + + +正则表达 相符的字符串举例 不相符字符串 + +^ab.*c$ abeec cabeec (如果用re.search(), 将无法找到。) + + +4)返回控制 + +我们有可能对搜索的结果进行进一步精简信息。比如下面一个正则表达式: + +output_(\d{4}) + +该正则表达式用括号()包围了一个小的正则表达式,\d{4}。 这个小的正则表达式被用于从结果中筛选想要的信息(在这里是四位数字)。这样被括号圈起来的正则表达式的一部分,称为群(group)。 +我们可以m.group(number)的方法来查询群。group(0)是整个正则表达的搜索结果,group(1)是第一个群…… +```python +import re +m = re.search("output_(\d{4})", "output_1986.txt") +print(m.group(1)) +``` + +我们还可以将群命名,以便更好地使用m.group查询: +```python +import re +m = re.search("output_(?P\d{4})", "output_1986.txt") #(?P...) 为group命名 +print(m.group("year")) +``` + +练习 +有一个文件,文件名为output_1981.10.21.txt 。下面使用Python: 读取文件名中的日期时间信息,并找出这一天是周几。将文件改名为output_YYYY-MM-DD-W.txt (YYYY:四位的年,MM:两位的月份,DD:两位的日,W:一位的周几,并假设周一为一周第一天) + + + + + +##总结 + +re.search() re.match() re.sub() re.findall() + +正则表达式构成方法 diff --git a/content/STL02.md b/content/STL02.md new file mode 100644 index 0000000..fa9a7ee --- /dev/null +++ b/content/STL02.md @@ -0,0 +1,115 @@ +#Python标准库02 时间与日期 (time, datetime包) + + + +Python具有良好的时间和日期管理功能。实际上,计算机只会维护一个挂钟时间(wall clock time),这个时间是从某个固定时间起点到现在的时间间隔。时间起点的选择与计算机相关,但一台计算机的话,这一时间起点是固定的。其它的日期信息都是从这一时间计算得到的。此外,计算机还可以测量CPU实际上运行的时间,也就是处理器时间(processor clock time),以测量计算机性能。当CPU处于闲置状态时,处理器时间会暂停。 + + + +##time包 + +time包基于C语言的库函数(library functions)。Python的解释器通常是用C编写的,Python的一些函数也会直接调用C语言的库函数。 +```python +import time +print(time.time()) # wall clock time, unit: second +print(time.clock()) # processor clock time, unit: second +``` + +time.sleep()可以将程序置于休眠状态,直到某时间间隔之后再唤醒程序,让程序继续运行。 +```python +import time +print('start') +time.sleep(10) # sleep for 10 seconds +print('wake up') +``` +当我们需要定时地查看程序运行状态时,就可以利用该方法。 + + + +time包还定义了struct_time对象。该对象实际上是将挂钟时间转换为年、月、日、时、分、秒……等日期信息,存储在该对象的各个属性中(tm_year, tm_mon, tm_mday...)。下面方法可以将挂钟时间转换为struct_time对象: +```python +st = time.gmtime() # 返回struct_time格式的UTC时间 +st = time.localtime() # 返回struct_time格式的当地时间, 当地时区根据系统环境决定。 + +s = time.mktime(st) # 将struct_time格式转换成wall clock time +``` + +##datetime包 + +1) 简介 + +datetime包是基于time包的一个高级包, 为我们提供了多一层的便利。 + +datetime可以理解为date和time两个组成部分。date是指年月日构成的日期(相当于日历),time是指时分秒微秒构成的一天24小时中的具体时间(相当于手表)。你可以将这两个分开管理(datetime.date类,datetime.time类),也可以将两者合在一起(datetime.datetime类)。由于其构造大同小异,我们将只介绍datetime.datetime类。 + +比如说我现在看到的时间,是2012年9月3日21时30分,我们可以用如下方式表达: +```python +import datetime +t = datetime.datetime(2012,9,3,21,30) +print(t) +``` +所返回的t有如下属性: + +hour, minute, second, microsecond + +year, month, day, weekday # weekday表示周几 + + + +2) 运算 + +datetime包还定义了时间间隔对象(timedelta)。一个时间点(datetime)加上一个时间间隔(timedelta)可以得到一个新的时间点(datetime)。比如今天的上午3点加上5个小时得到今天的上午8点。同理,两个时间点相减会得到一个时间间隔。 + +```python +import datetime +t = datetime.datetime(2012,9,3,21,30) +t_next = datetime.datetime(2012,9,5,23,30) +delta1 = datetime.timedelta(seconds = 600) +delta2 = datetime.timedelta(weeks = 3) +print(t + delta1) +print(t + delta2) +print(t_next - t) +``` +在给datetime.timedelta传递参数(如上的seconds和weeks)的时候,还可以是days, hours, milliseconds, microseconds。 + + + +两个datetime对象还可以进行比较。比如使用上面的t和t_next: +```python +print(t > t_next) +``` + +3) datetime对象与字符串转换 + +假如我们有一个的字符串,我们如何将它转换成为datetime对象呢? + +一个方法是用上一讲的正则表达式来搜索字符串。但时间信息实际上有很明显的特征,我们可以用格式化读取的方式读取时间信息。 +```python +from datetime import datetime +format = "output-%Y-%m-%d-%H%M%S.txt" +str = "output-1997-12-23-030000.txt" +t = datetime.strptime(str, format) +strptime, p = parsing +``` + +我们通过format来告知Python我们的str字符串中包含的日期的格式。在format中,%Y表示年所出现的位置, %m表示月份所出现的位置……。 + +反过来,我们也可以调用datetime对象的strftime()方法,来将datetime对象转换为特定格式的字符串。比如上面所定义的t_next, +```python +print(t_next.strftime(format)) +``` +strftime, f = formatting + +具体的格式写法可参阅官方文档。 如果是Linux系统,也可查阅date命令的手册($man date),两者相通。 + + + +##总结 + +时间,休眠 + +datetime, timedelta + +格式化时间 + + diff --git a/content/STL03.md b/content/STL03.md new file mode 100644 index 0000000..1cc81ef --- /dev/null +++ b/content/STL03.md @@ -0,0 +1,85 @@ +#Python标准库03 路径与文件 (os.path包, glob包) + + + + + +路径与文件的简介请参看Linux文件系统 + + + +##os.path包 + +os.path包主要是处理路径字符串,比如说'/home/vamei/doc/file.txt',提取出有用信息。 + +```python +import os.path +path = '/home/vamei/doc/file.txt' + +print(os.path.basename(path)) # 查询路径中包含的文件名 +print(os.path.dirname(path)) # 查询路径中包含的目录 + +info = os.path.split(path) # 将路径分割成文件名和目录两个部分,放在一个表中返回 +path2 = os.path.join('/', 'home', 'vamei', 'doc', 'file1.txt') # 使用目录名和文件名构成一个路径字符串 + +p_list = [path, path2] +print(os.path.commonprefix(p_list)) # 查询多个路径的共同部分 +``` +此外,还有下面的方法: + +os.path.normpath(path) # 去除路径path中的冗余。比如'/home/vamei/../.'被转化为'/home' + + + +os.path还可以查询文件的相关信息(metadata)。文件的相关信息不存储在文件内部,而是由操作系统维护的,关于文件的一些信息(比如文件类型,大小,修改时间)。 + +```python +import os.path +path = '/home/vamei/doc/file.txt' + +print(os.path.exists(path)) # 查询文件是否存在 + +print(os.path.getsize(path)) # 查询文件大小 +print(os.path.getatime(path)) # 查询文件上一次读取的时间 +print(os.path.getmtime(path)) # 查询文件上一次修改的时间 + +print(os.path.isfile(path)) # 路径是否指向常规文件 +print(os.path.isdir(path)) # 路径是否指向目录文件 +``` + (实际上,这一部份类似于Linux中的ls命令的某些功能) + + + +##glob包 + +glob包最常用的方法只有一个, glob.glob()。该方法的功能与Linux中的ls相似(参看Linux文件管理命令),接受一个Linux式的文件名格式表达式(filename pattern expression),列出所有符合该表达式的文件(与正则表达式类似),将所有文件名放在一个表中返回。所以glob.glob()是一个查询目录下文件的好方法。 + +该文件名表达式的语法与Python自身的正则表达式不同 (你可以同时看一下fnmatch包,它的功能是检测一个文件名是否符合Linux的文件名格式表达式)。 如下: + +Filename Pattern Expression Python Regular Expression + +* .* + +? . + +[0-9] same + +[a-e] same + +[^mnp] same + + + +我们可以用该命令找出/home/vamei下的所有文件: +```python +import glob +print(glob.glob('/home/vamei/*')) +``` + +##总结 + +文件系统 + +os.path + +glob.glob diff --git a/content/STL04.md b/content/STL04.md new file mode 100644 index 0000000..e05460e --- /dev/null +++ b/content/STL04.md @@ -0,0 +1,97 @@ +#Python标准库04 文件管理 (部分os包,shutil包) + + + +在操作系统下,用户可以通过操作系统的命令来管理文件,参考linux文件管理相关命令。Python标准库则允许我们从Python内部管理文件。相同的目的,我们有了两条途径。尽管在Python调用标准库的方式不如操作系统命令直接,但有它自己的优势。你可以利用Python语言,并发挥其他Python工具,形成组合的文件管理功能。Python or Shell? 这是留给用户的选择。本文中会尽量将两者相似的功能相对应。 + +本文基于linux文件管理背景知识 + + + +##os包 + +os包包括各种各样的函数,以实现操作系统的许多功能。这个包非常庞杂。os包的一些命令就是用于文件管理。我们这里列出最常用的: + +mkdir(path) + +创建新目录,path为一个字符串,表示新目录的路径。相当于$mkdir命令 + +rmdir(path) + +删除空的目录,path为一个字符串,表示想要删除的目录的路径。相当于$rmdir命令 + +listdir(path) + +返回目录中所有文件。相当于$ls命令。 + + + +remove(path) + +删除path指向的文件。 + +rename(src, dst) + +重命名文件,src和dst为两个路径,分别表示重命名之前和之后的路径。 + + + +chmod(path, mode) + +改变path指向的文件的权限。相当于$chmod命令。 + +chown(path, uid, gid) + +改变path所指向文件的拥有者和拥有组。相当于$chown命令。 + +stat(path) + +查看path所指向文件的附加信息,相当于$ls -l命令。 + +symlink(src, dst) + +为文件dst创建软链接,src为软链接文件的路径。相当于$ln -s命令。 + + + +getcwd() + +查询当前工作路径 (cwd, current working directory),相当于$pwd命令。 + + + +比如说我们要新建目录new: +```python +import os +os.mkdir('/home/vamei/new') +``` + +##shutil包 + +copy(src, dst) + +复制文件,从src到dst。相当于$cp命令。 + +move(src, dst) + +移动文件,从src到dst。相当于$mv命令。 + + + +比如我们想复制文件a.txt: +```python +import shutil +shutil.copy('a.txt', 'b.txt') +``` + +想深入细节,请参照官方文档os, shutil。 + +结合本章以及之前的内容,我们把Python打造成一个文件管理的利器了。 + + + +##总结 + +os包: rmdir, mkdir, listdir, remove, rename, chmod, chown, stat, symlink + +shutil包: copy, move diff --git a/content/STL05.md b/content/STL05.md new file mode 100644 index 0000000..c19bf64 --- /dev/null +++ b/content/STL05.md @@ -0,0 +1,90 @@ +#Python标准库05 存储对象 (pickle包,cPickle包) + + + + + +在之前对Python对象的介绍中 (面向对象的基本概念,面向对象的进一步拓展),我提到过Python“一切皆对象”的哲学,在Python中,无论是变量还是函数,都是一个对象。当Python运行时,对象存储在内存中,随时等待系统的调用。然而,内存里的数据会随着计算机关机和消失,如何将对象保存到文件,并储存在硬盘上呢? + +计算机的内存中存储的是二进制的序列 (当然,在Linux眼中,是文本流)。我们可以直接将某个对象所对应位置的数据抓取下来,转换成文本流 (这个过程叫做serialize),然后将文本流存入到文件中。由于Python在创建对象时,要参考对象的类定义,所以当我们从文本中读取对象时,必须在手边要有该对象的类定义,才能懂得如何去重建这一对象。从文件读取时,对于Python的内建(built-in)对象 (比如说整数、词典、表等等),由于其类定义已经载入内存,所以不需要我们再在程序中定义类。但对于用户自行定义的对象,就必须要先定义类,然后才能从文件中载入对象 (比如面向对象的基本概念中的对象那个summer)。 + + + +##pickle包 + +对于上述过程,最常用的工具是Python中的pickle包。 + +1) 将内存中的对象转换成为文本流: + +```pyhton +import pickle + +# define class +class Bird(object): + have_feather = True + way_of_reproduction = 'egg' + +summer = Bird() # construct an object +picklestring = pickle.dumps(summer) # serialize object +``` +使用pickle.dumps()方法可以将对象summer转换成了字符串 picklestring(也就是文本流)。随后我们可以用普通文本的存储方法来将该字符串储存在文件(文本文件的输入输出)。 + + + +当然,我们也可以使用pickle.dump()的方法,将上面两部合二为一: + +```python +import pickle + +# define class +class Bird(object): + have_feather = True + way_of_reproduction = 'egg' + +summer = Bird() # construct an object +fn = 'a.pkl' +with open(fn, 'w') as f: # open file with write-mode + picklestring = pickle.dump(summer, f) # serialize and save object +``` +对象summer存储在文件a.pkl + + + +2) 重建对象 + +首先,我们要从文本中读出文本,存储到字符串 (文本文件的输入输出)。然后使用pickle.loads(str)的方法,将字符串转换成为对象。要记得,此时我们的程序中必须已经有了该对象的类定义。 + + + +此外,我们也可以使用pickle.load()的方法,将上面步骤合并: + +```python +import pickle + +# define the class before unpickle +class Bird(object): + have_feather = True + way_of_reproduction = 'egg' + +fn = 'a.pkl' +with open(fn, 'r') as f: + summer = pickle.load(f) # read file and build object +``` + + + + +##cPickle包 + +cPickle包的功能和用法与pickle包几乎完全相同 (其存在差别的地方实际上很少用到),不同在于cPickle是基于c语言编写的,速度是pickle包的1000倍。对于上面的例子,如果想使用cPickle包,我们都可以将import语句改为: + +import cPickle as pickle +就不需要再做任何改动了。 + + + +##总结 + +对象 -> 文本 -> 文件 + +pickle.dump(), pickle.load(), cPickle diff --git a/content/STL06.md b/content/STL06.md new file mode 100644 index 0000000..fea726f --- /dev/null +++ b/content/STL06.md @@ -0,0 +1,163 @@ +#Python标准库06 子进程 (subprocess包) + + + + +这里的内容以Linux进程基础和Linux文本流为基础。subprocess包主要功能是执行外部的命令和程序。比如说,我需要使用wget下载文件。我在Python中调用wget程序。从这个意义上来说,subprocess的功能与shell类似。 + + + +##subprocess以及常用的封装函数 + +当我们运行python的时候,我们都是在创建并运行一个进程。正如我们在Linux进程基础中介绍的那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序(fork,exec见Linux进程基础)。 + + + +subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。 + + + +使用subprocess包中的函数创建子进程的时候,要注意: + +1) 在创建子进程之后,父进程是否暂停,并等待子进程运行。 + +2) 函数返回什么 + +3) 当returncode不为0时,父进程如何处理。 + + + +subprocess.call() +父进程等待子进程完成 +返回退出信息(returncode,相当于exit code,见Linux进程基础) + + + +subprocess.check_call() + +父进程等待子进程完成 + +返回0 + +检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try...except...来检查(见Python错误处理)。 + + + +subprocess.check_output() + +父进程等待子进程完成 + +返回子进程向标准输出的输出结果 + +检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try...except...来检查。 + + + +这三个函数的使用方法相类似,我们以subprocess.call()来说明: +```python +import subprocess +rc = subprocess.call(["ls","-l"]) +``` +我们将程序名(ls)和所带的参数(-l)一起放在一个表中传递给subprocess.call() + + + +可以通过一个shell来解释一整个字符串: +```python +import subprocess +out = subprocess.call("ls -l", shell=True) +out = subprocess.call("cd ..", shell=True) +``` +我们使用了shell=True这个参数。这个时候,我们使用一整个字符串,而不是一个表来运行子进程。Python将先运行一个shell,再用这个shell来解释这整个字符串。 + +shell命令中有一些是shell的内建命令,这些命令必须通过shell运行,$cd。shell=True允许我们运行这样一些命令。 + + + +##Popen() + +实际上,我们上面的三个函数都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。 + + + +与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block): +```python +import subprocess +child = subprocess.Popen(["ping","-c","5","www.google.com"]) +print("parent process") +``` +从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。 + + + +对比等待的情况: +```python +import subprocess +child = subprocess.Popen(["ping","-c","5","www.google.com"]) +child.wait() +print("parent process") +``` + +此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象: + +child.poll() # 检查子进程状态 + +child.kill() # 终止子进程 + +child.send_signal() # 向子进程发送信号 + +child.terminate() # 终止子进程 + + + +子进程的PID存储在child.pid + + + +##子进程的文本流控制 + +(沿用child子进程) 子进程的标准输入,标准输出和标准错误也可以通过如下属性表示: + +child.stdin + +child.stdout + +child.stderr + + + +我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe): +```python +import subprocess +child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE) +child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE) +out = child2.communicate() +print(out) +``` +subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。 + +要注意的是,communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成。 + + + +我们还可以利用communicate()方法来使用PIPE给子进程输入: +```python +import subprocess +child = subprocess.Popen(["cat"], stdin=subprocess.PIPE) +child.communicate("vamei") +``` +我们启动子进程之后,cat会等待输入,直到我们用communicate()输入"vamei"。 + + + +通过使用subprocess包,我们可以运行外部程序。这极大的拓展了Python的功能。如果你已经了解了操作系统的某些应用,你可以从Python中直接调用该应用(而不是完全依赖Python),并将应用的结果输出给Python,并让Python继续处理。shell的功能(比如利用文本流连接各个应用),就可以在Python中实现。 + + + +##总结 + +subprocess.call, subprocess.check_call(), subprocess.check_output() + +subprocess.Popen(), subprocess.PIPE + +Popen.wait(), Popen.communicate() diff --git a/content/STL07.md b/content/STL07.md new file mode 100644 index 0000000..0890694 --- /dev/null +++ b/content/STL07.md @@ -0,0 +1,107 @@ +#Python标准库07 信号 (signal包,部分os包) + + + +在了解了Linux的信号基础之后,Python标准库中的signal包就很容易学习和理解。signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等。要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windows内核中由于对信号机制的支持不充分,所以在Windows上的Python不能发挥信号系统的功能。 + + + +##定义信号名 + +signal包定义了各个信号名及其对应的整数,比如 +```python +import signal +print signal.SIGALRM +print signal.SIGCONT +``` +Python所用的信号名和Linux一致。你可以通过 +```bash +$man 7 signal +``` +查询 + + + +##预设信号处理函数 + +signal包的核心是使用signal.signal()函数来预设(register)信号处理函数,如下所示: + +singnal.signal(signalnum, handler) + +signalnum为某个信号,handler为该信号的处理函数。我们在信号基础里提到,进程可以无视信号,可以采取默认操作,还可以自定义操作。当handler为signal.SIG_IGN时,信号被无视(ignore)。当handler为singal.SIG_DFL,进程采取默认操作(default)。当handler为一个函数名时,进程采取函数中定义的操作。 + +```python +import signal +# Define signal handler function +def myHandler(signum, frame): + print('I received: ', signum) + +# register signal.SIGTSTP's handler +signal.signal(signal.SIGTSTP, myHandler) +signal.pause() +print('End of Signal Demo') +``` + + +在主程序中,我们首先使用signal.signal()函数来预设信号处理函数。然后我们执行signal.pause()来让该进程暂停以等待信号,以等待信号。当信号SIGUSR1被传递给该进程时,进程从暂停中恢复,并根据预设,执行SIGTSTP的信号处理函数myHandler()。myHandler的两个参数一个用来识别信号(signum),另一个用来获得信号发生时,进程栈的状况(stack frame)。这两个参数都是由signal.singnal()函数来传递的。 + +上面的程序可以保存在一个文件中(比如test.py)。我们使用如下方法运行: + +$python test.py + +以便让进程运行。当程序运行到signal.pause()的时候,进程暂停并等待信号。此时,通过按下CTRL+Z向该进程发送SIGTSTP信号。我们可以看到,进程执行了myHandle()函数, 随后返回主程序,继续执行。(当然,也可以用$ps查询process ID, 再使用$kill来发出信号。) + +(进程并不一定要使用signal.pause()暂停以等待信号,它也可以在进行工作中接受信号,比如将上面的signal.pause()改为一个需要长时间工作的循环。) + + + +我们可以根据自己的需要更改myHandler()中的操作,以针对不同的信号实现个性化的处理。 + + + +##定时发出SIGALRM信号 + +一个有用的函数是signal.alarm(),它被用于在一定时间之后,向进程自身发送SIGALRM信号: + +```python +import signal +# Define signal handler function +def myHandler(signum, frame): + print("Now, it's the time") + exit() + +# register signal.SIGALRM's handler +signal.signal(signal.SIGALRM, myHandler) +signal.alarm(5) +while True: + print('not yet') +``` +我们这里用了一个无限循环以便让进程持续运行。在signal.alarm()执行5秒之后,进程将向自己发出SIGALRM信号,随后,信号处理函数myHandler开始执行。 + + + +##发送信号 + +signal包的核心是设置信号处理函数。除了signal.alarm()向自身发送信号之外,并没有其他发送信号的功能。但在os包中,有类似于linux的kill命令的函数,分别为 + +os.kill(pid, sid) + +os.killpg(pgid, sid) + +分别向进程和进程组(见Linux进程关系)发送信号。sid为信号所对应的整数或者singal.SIG*。 + + + +实际上signal, pause,kill和alarm都是Linux应用编程中常见的C库函数,在这里,我们只不过是用Python语言来实现了一下。实际上,Python 的解释器是使用C语言来编写的,所以有此相似性也并不意外。此外,在Python 3.4中,signal包被增强,信号阻塞等功能被加入到该包中。我们暂时不深入到该包中。 + + + +##总结 + +signal.SIG* + +signal.signal() + +signal.pause() + +signal.alarm() diff --git a/content/STL08.md b/content/STL08.md new file mode 100644 index 0000000..7b185da --- /dev/null +++ b/content/STL08.md @@ -0,0 +1,143 @@ +##Python标准库08 多线程与同步 (threading包) + + + + +Python主要通过标准库中的threading包来实现多线程。在当今网络时代,每个服务器都会接收到大量的请求。服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率。Python是一种网络服务器的后台工作语言 (比如豆瓣网),所以多线程也就很自然被Python语言支持。 + +(关于多线程的原理和C实现方法,请参考我之前写的Linux多线程与同步,要了解race condition, mutex和condition variable的概念) + + + +##多线程售票以及同步 + +我们使用Python来实现Linux多线程与同步文中的售票程序。我们使用mutex (也就是Python中的Lock类对象) 来实现线程的同步: + +```python +# A program to simulate selling tickets in multi-thread way +# Written by Vamei + +import threading +import time +import os + +# This function could be any function to do other chores. +def doChore(): + time.sleep(0.5) + +# Function for each thread +def booth(tid): + global i + global lock + while True: + lock.acquire() # Lock; or wait if other thread is holding the lock + if i != 0: + i = i - 1 # Sell tickets + print(tid,':now left:',i) # Tickets left + doChore() # Other critical operations + else: + print("Thread_id",tid," No more tickets") + os._exit(0) # Exit the whole process immediately + lock.release() # Unblock + doChore() # Non-critical operations + +# Start of the main function +i = 100 # Available ticket number +lock = threading.Lock() # Lock (i.e., mutex) + +# Start 10 threads +for k in range(10): + new_thread = threading.Thread(target=booth,args=(k,)) # Set up thread; target: the callable (function) to be run, args: the argument for the callable + new_thread.start() # run the thread +``` + +我们使用了两个全局变量,一个是i,用以储存剩余票数;一个是lock对象,用于同步线程对i的修改。此外,在最后的for循环中,我们总共设置了10个线程。每个线程都执行booth()函数。线程在调用start()方法的时候正式启动 (实际上,计算机中最多会有11个线程,因为主程序本身也会占用一个线程)。Python使用threading.Thread对象来代表线程,用threading.Lock对象来代表一个互斥锁 (mutex)。 + +有两点需要注意: + +我们在函数中使用global来声明变量为全局变量,从而让多线程共享i和lock (在C语言中,我们通过将变量放在所有函数外面来让它成为全局变量)。如果不这么声明,由于i和lock是不可变数据对象,它们将被当作一个局部变量(参看Python动态类型)。如果是可变数据对象的话,则不需要global声明。我们甚至可以将可变数据对象作为参数来传递给线程函数。这些线程将共享这些可变数据对象。 +我们在booth中使用了两个doChore()函数。可以在未来改进程序,以便让线程除了进行i=i-1之外,做更多的操作,比如打印剩余票数,找钱,或者喝口水之类的。第一个doChore()依然在Lock内部,所以可以安全地使用共享资源 (critical operations, 比如打印剩余票数)。第二个doChore()时,Lock已经被释放,所以不能再去使用共享资源。这时候可以做一些不使用共享资源的操作 (non-critical operation, 比如找钱、喝水)。我故意让doChore()等待了0.5秒,以代表这些额外的操作可能花费的时间。你可以定义的函数来代替doChore()。 + + +##OOP创建线程 + +上面的Python程序非常类似于一个面向过程的C程序。我们下面介绍如何通过面向对象 (OOP, object-oriented programming,参看Python面向对象的基本概念和Python面向对象的进一步拓展) 的方法实现多线程,其核心是继承threading.Thread类。我们上面的for循环中已经利用了threading.Thread()的方法来创建一个Thread对象,并将函数booth()以及其参数传递给改对象,并调用start()方法来运行线程。OOP的话,通过修改Thread类的run()方法来定义线程所要执行的命令。 + +```python +# A program to simulate selling tickets in multi-thread way +# Written by Vamei + +import threading +import time +import os + +# This function could be any function to do other chores. +def doChore(): + time.sleep(0.5) + +# Function for each thread +class BoothThread(threading.Thread): + def __init__(self, tid, monitor): + self.tid = tid + self.monitor = monitor + threading.Thread.__init__(self) + def run(self): + while True: + monitor['lock'].acquire() # Lock; or wait if other thread is holding the lock + if monitor['tick'] != 0: + monitor['tick'] = monitor['tick'] - 1 # Sell tickets + print(self.tid,':now left:',monitor['tick']) # Tickets left + doChore() # Other critical operations + else: + print("Thread_id",self.tid," No more tickets") + os._exit(0) # Exit the whole process immediately + monitor['lock'].release() # Unblock + doChore() # Non-critical operations + +# Start of the main function +monitor = {'tick':100, 'lock':threading.Lock()} + +# Start 10 threads +for k in range(10): + new_thread = BoothThread(k, monitor) + new_thread.start() +``` +我们自己定义了一个类BoothThread, 这个类继承自thread.Threading类。然后我们把上面的booth()所进行的操作统统放入到BoothThread类的run()方法中。注意,我们没有使用全局变量声明global,而是使用了一个词典monitor存放全局变量,然后把词典作为参数传递给线程函数。由于词典是可变数据对象,所以当它被传递给函数的时候,函数所使用的依然是同一个对象,相当于被多个线程所共享。这也是多线程乃至于多进程编程的一个技巧 (应尽量避免上面的global声明的用法,因为它并不适用于windows平台)。 + +上面OOP编程方法与面向过程的编程方法相比,并没有带来太大实质性的差别。 + + + +##其他 + +threading.Thread对象: 我们已经介绍了该对象的start()和run(), 此外: + +join()方法,调用该方法的线程将等待直到改Thread对象完成,再恢复运行。这与进程间调用wait()函数相类似。 + + +下面的对象用于处理多线程同步。对象一旦被建立,可以被多个线程共享,并根据情况阻塞某些进程。请与Linux多线程与同步中的同步工具参照阅读。 + +threading.Lock对象: mutex, 有acquire()和release()方法。 + +threading.Condition对象: condition variable,建立该对象时,会包含一个Lock对象 (因为condition variable总是和mutex一起使用)。可以对Condition对象调用acquire()和release()方法,以控制潜在的Lock对象。此外: + +wait()方法,相当于cond_wait() +notify_all(),相当与cond_broadcast() +nofify(),与notify_all()功能类似,但只唤醒一个等待的线程,而不是全部 +threading.Semaphore对象: semaphore,也就是计数锁(semaphore传统意义上是一种进程间同步工具,见Linux进程间通信)。创建对象的时候,可以传递一个整数作为计数上限 (sema = threading.Semaphore(5))。它与Lock类似,也有Lock的两个方法。 +threading.Event对象: 与threading.Condition相类似,相当于没有潜在的Lock保护的condition variable。对象有True和False两个状态。可以多个线程使用wait()等待,直到某个线程调用该对象的set()方法,将对象设置为True。线程可以调用对象的clear()方法来重置对象为False状态。 + + +练习 +参照Linux多线程与同步中的condition variable的例子,使用Python实现。同时考虑使用面向过程和面向对象的编程方法。 +更多的threading的内容请参考: +http://docs.python.org/library/threading.html + + + +##总结 + +threading.Thread + +Lock, Condition, Semaphore, Event + diff --git a/content/STL09.md b/content/STL09.md new file mode 100644 index 0000000..d213a3b --- /dev/null +++ b/content/STL09.md @@ -0,0 +1,106 @@ + +##Python标准库09 当前进程信息 (部分os包) + + + + +我们在Linux的概念与体系,多次提及进程的重要性。Python的os包中有查询和修改进程信息的函数。学习Python的这些工具也有助于理解Linux体系。 + + + +##进程信息 + +os包中相关函数如下: + +uname() 返回操作系统相关信息。类似于Linux上的uname命令。 + +umask() 设置该进程创建文件时的权限mask。类似于Linux上的umask命令,见Linux文件管理背景知识 + + + +get*() 查询 (*由以下代替) + + uid, euid, resuid, gid, egid, resgid :权限相关,其中resuid主要用来返回saved UID。相关介绍见Linux用户与“最小权限”原则 + + pid, pgid, ppid, sid :进程相关。相关介绍见Linux进程关系 + + + +put*() 设置 (*由以下代替) + + euid, egid: 用于更改euid,egid。 + + uid, gid : 改变进程的uid, gid。只有super user才有权改变进程uid和gid (意味着要以$sudo python的方式运行Python)。 + + pgid, sid : 改变进程所在的进程组(process group)和会话(session)。 + + + +getenviron():获得进程的环境变量 + +setenviron():更改进程的环境变量 + + + +例1,进程的real UID和real GID +```python +import os +print(os.getuid()) +print(os.getgid()) +``` +将上面的程序保存为py_id.py文件,分别用$python py_id.py和$sudo python py_id.py看一下运行结果 + + + +##saved UID和saved GID + +我们希望saved UID和saved GID如我们在Linux用户与“最小权限”原则中描述的那样工作,但这很难。原因在于,当我们写一个Python脚本后,我们实际运行的是python这个解释器,而不是Python脚本文件。对比C,C语言直接运行由C语言编译成的执行文件。我们必须更改python解释器本身的权限来运用saved UID机制,然而这么做又是异常危险的。 + +比如说,我们的python执行文件为/usr/bin/python (你可以通过$which python获知) + +我们先看一下 + +$ls -l /usr/bin/python + +的结果: + +-rwxr-xr-x root root + + + +我们修改权限以设置set UID和set GID位 (参考Linux用户与“最小权限”原则) + +$sudo chmod 6755 /usr/bin/python + +/usr/bin/python的权限成为: + +-rwsr-sr-x root root + + + +随后,我们运行文件下面test.py文件,这个文件可以是由普通用户vamei所有: +```python +import os +print(os.getresuid()) +``` +我们得到结果: + +(1000, 0, 0) + +上面分别是UID,EUID,saved UID。我们只用执行一个由普通用户拥有的python脚本,就可以得到super user的权限!所以,这样做是极度危险的,我们相当于交出了系统的保护系统。想像一下Python强大的功能,别人现在可以用这些强大的功能作为攻击你的武器了!使用下面命令来恢复到从前: + +$sudo chmod 0755 /usr/bin/python + + + +关于脚本文件的saved UID/GID,更加详细的讨论见 + +http://www.faqs.org/faqs/unix-faq/faq/part4/section-7.html + + + +##总结 + +get*, set* + +umask(), uname() diff --git a/content/STL10.md b/content/STL10.md new file mode 100644 index 0000000..0586d1a --- /dev/null +++ b/content/STL10.md @@ -0,0 +1,172 @@ +##Python标准库10 多进程初步 (multiprocessing包) + + + + + +我们已经见过了使用subprocess包来创建子进程,但这个包有两个很大的局限性:1) 我们总是让subprocess运行外部的程序,而不是运行一个Python脚本内部编写的函数。2) 进程间只通过管道进行文本交流。以上限制了我们将subprocess包应用到更广泛的多进程任务。(这样的比较实际是不公平的,因为subprocessing本身就是设计成为一个shell,而不是一个多进程管理包) + + + +##threading和multiprocessing + +(请尽量先阅读Python多线程与同步) + +multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。 + +但在使用这些共享API的时候,我们要注意以下几点: + +在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以,有必要对每个Process对象调用join()方法 (实际上等同于wait)。对于多线程来说,由于只有一个进程,所以不存在此必要性。 +multiprocessing提供了threading包中没有的IPC(比如Pipe和Queue),效率上更高。应优先考虑Pipe和Queue,避免使用Lock/Event/Semaphore/Condition等同步方式 (因为它们占据的不是用户进程的资源)。 +多进程应该避免共享资源。在多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以上方法并不合适。此时我们可以通过共享内存和Manager的方法来共享资源。但这样做提高了程序的复杂度,并因为同步的需要而降低了程序的效率。 +Process.PID中保存有PID,如果进程还没有start(),则PID为None。 + + + +我们可以从下面的程序中看到Thread对象和Process对象在使用上的相似性与结果上的不同。各个线程和进程都做一件事:打印PID。但问题是,所有的任务在打印的时候都会向同一个标准输出(stdout)输出。这样输出的字符会混合在一起,无法阅读。使用Lock同步,在一个任务输出完成之后,再允许另一个任务输出,可以避免多个任务同时向终端输出。 + +```python +# Similarity and difference of multi thread vs. multi process +# Written by Vamei + +import os +import threading +import multiprocessing + +# worker function +def worker(sign, lock): + lock.acquire() + print(sign, os.getpid()) + lock.release() + +# Main +print('Main:',os.getpid()) + +# Multi-thread +record = [] +lock = threading.Lock() +for i in range(5): + thread = threading.Thread(target=worker,args=('thread',lock)) + thread.start() + record.append(thread) + +for thread in record: + thread.join() + +# Multi-process +record = [] +lock = multiprocessing.Lock() +for i in range(5): + process = multiprocessing.Process(target=worker,args=('process',lock)) + process.start() + record.append(process) + +for process in record: + process.join() +``` +所有Thread的PID都与主程序相同,而每个Process都有一个不同的PID。 + +(练习: 使用mutiprocessing包将Python多线程与同步中的多线程程序更改为多进程程序) + + + +##Pipe和Queue + +正如我们在Linux多线程中介绍的管道PIPE和消息队列message queue,multiprocessing包中有Pipe类和Queue类来分别支持这两种IPC机制。Pipe和Queue可以用来传送常见的对象。 + + + +1) Pipe可以是单向(half-duplex),也可以是双向(duplex)。我们通过mutiprocessing.Pipe(duplex=False)创建单向管道 (默认为双向)。一个进程从PIPE一端输入对象,然后被PIPE另一端的进程接收,单向管道只允许管道一端的进程输入,而双向管道则允许从两端输入。 + +下面的程序展示了Pipe的使用: + + + +```python +# Multiprocessing with Pipe +# Written by Vamei + +import multiprocessing as mul + +def proc1(pipe): + pipe.send('hello') + print('proc1 rec:',pipe.recv()) + +def proc2(pipe): + print('proc2 rec:',pipe.recv()) + pipe.send('hello, too') + +# Build a pipe +pipe = mul.Pipe() + +# Pass an end of the pipe to process 1 +p1 = mul.Process(target=proc1, args=(pipe[0],)) +# Pass the other end of the pipe to process 2 +p2 = mul.Process(target=proc2, args=(pipe[1],)) +p1.start() +p2.start() +p1.join() +p2.join() +``` +这里的Pipe是双向的。 + +Pipe对象建立的时候,返回一个含有两个元素的表,每个元素代表Pipe的一端(Connection对象)。我们对Pipe的某一端调用send()方法来传送对象,在另一端使用recv()来接收。 + + + +2) Queue与Pipe相类似,都是先进先出的结构。但Queue允许多个进程放入,多个进程从队列取出对象。Queue使用mutiprocessing.Queue(maxsize)创建,maxsize表示队列中可以存放对象的最大数量。 +下面的程序展示了Queue的使用: + +```python +# Written by Vamei +import os +import multiprocessing +import time +#================== +# input worker +def inputQ(queue): + info = str(os.getpid()) + '(put):' + str(time.time()) + queue.put(info) + +# output worker +def outputQ(queue,lock): + info = queue.get() + lock.acquire() + print (str(os.getpid()) + '(get):' + info) + lock.release() +#=================== +# Main +record1 = [] # store input processes +record2 = [] # store output processes +lock = multiprocessing.Lock() # To prevent messy print +queue = multiprocessing.Queue(3) + +# input processes +for i in range(10): + process = multiprocessing.Process(target=inputQ,args=(queue,)) + process.start() + record1.append(process) + +# output processes +for i in range(10): + process = multiprocessing.Process(target=outputQ,args=(queue,lock)) + process.start() + record2.append(process) + +for p in record1: + p.join() + +queue.close() # No more object will come, close the queue + +for p in record2: + p.join() +``` + 一些进程使用put()在Queue中放入字符串,这个字符串中包含PID和时间。另一些进程从Queue中取出,并打印自己的PID以及get()的字符串。 + + + +##总结 + +Process, Lock, Event, Semaphore, Condition + +Pipe, Queue diff --git a/content/STL11.md b/content/STL11.md new file mode 100644 index 0000000..14faf70 --- /dev/null +++ b/content/STL11.md @@ -0,0 +1,135 @@ + +##Python标准库11 多进程探索 (multiprocessing包) + + + + +在初步了解Python多进程之后,我们可以继续探索multiprocessing包中更加高级的工具。这些工具可以让我们更加便利地实现多进程。 + + + +##进程池 + +进程池 (Process Pool)可以创建多个进程。这些进程就像是随时待命的士兵,准备执行任务(程序)。一个进程池中可以容纳多个待命的士兵。 + + + + + +“三个进程的进程池” + + + + + +比如下面的程序: + +```python +import multiprocessing as mul + +def f(x): + return x**2 + +pool = mul.Pool(5) +rel = pool.map(f,[1,2,3,4,5,6,7,8,9,10]) +print(rel) +``` +我们创建了一个容许5个进程的进程池 (Process Pool) 。Pool运行的每个进程都执行f()函数。我们利用map()方法,将f()函数作用到表的每个元素上。这与built-in的map()函数类似,只是这里用5个进程并行处理。如果进程运行结束后,还有需要处理的元素,那么的进程会被用于重新运行f()函数。除了map()方法外,Pool还有下面的常用方法。 + +apply_async(func,args) 从进程池中取出一个进程执行func,args为func的参数。它将返回一个AsyncResult的对象,你可以对该对象调用get()方法以获得结果。 + +close() 进程池不再创建新的进程 + +join() wait进程池中的全部进程。必须对Pool先调用close()方法才能join。 + + + +##练习 + +有下面一个文件download.txt。 + +www.sina.com.cn +www.163.com +www.iciba.com +www.cnblogs.com +www.qq.com +www.douban.com +使用包含3个进程的进程池下载文件中网站的首页。(你可以使用subprocess调用wget或者curl等下载工具执行具体的下载任务) + + + +##共享资源 + +我们在Python多进程初步已经提到,我们应该尽量避免多进程共享资源。多进程共享资源必然会带来进程间相互竞争。而这种竞争又会造成race condition,我们的结果有可能被竞争的不确定性所影响。但如果需要,我们依然可以通过共享内存和Manager对象这么做。 + + + + + +共享“资源” + +共享内存 + +在Linux进程间通信中,我们已经讲述了共享内存(shared memory)的原理,这里给出用Python实现的例子: + +```python +# modified from official documentation +import multiprocessing + +def f(n, a): + n.value = 3.14 + a[0] = 5 + +num = multiprocessing.Value('d', 0.0) +arr = multiprocessing.Array('i', range(10)) + +p = multiprocessing.Process(target=f, args=(num, arr)) +p.start() +p.join() + +print num.value +print arr[:] +``` +这里我们实际上只有主进程和Process对象代表的进程。我们在主进程的内存空间中创建共享的内存,也就是Value和Array两个对象。对象Value被设置成为双精度数(d), 并初始化为0.0。而Array则类似于C中的数组,有固定的类型(i, 也就是整数)。在Process进程中,我们修改了Value和Array对象。回到主程序,打印出结果,主程序也看到了两个对象的改变,说明资源确实在两个进程之间共享。 + + + +Manager + +Manager对象类似于服务器与客户之间的通信 (server-client),与我们在Internet上的活动很类似。我们用一个进程作为服务器,建立Manager来真正存放资源。其它的进程可以通过参数传递或者根据地址来访问Manager,建立连接后,操作服务器上的资源。在防火墙允许的情况下,我们完全可以将Manager运用于多计算机,从而模仿了一个真实的网络情境。下面的例子中,我们对Manager的使用类似于shared memory,但可以共享更丰富的对象类型。 + +```python +import multiprocessing + +def f(x, arr, l): + x.value = 3.14 + arr[0] = 5 + l.append('Hello') + +server = multiprocessing.Manager() +x = server.Value('d', 0.0) +arr = server.Array('i', range(10)) +l = server.list() + +proc = multiprocessing.Process(target=f, args=(x, arr, l)) +proc.start() +proc.join() + +print(x.value) +print(arr) +print(l) +``` +Manager利用list()方法提供了表的共享方式。实际上你可以利用dict()来共享词典,Lock()来共享threading.Lock(注意,我们共享的是threading.Lock,而不是进程的mutiprocessing.Lock。后者本身已经实现了进程共享)等。 这样Manager就允许我们共享更多样的对象。 + + + +我们在这里不深入讲解Manager在远程情况下的应用。有机会的话,会在网络应用中进一步探索。 + + + +##总结 + +Pool + +Shared memory, Manager + diff --git a/content/STL12.md b/content/STL12.md new file mode 100644 index 0000000..92ed492 --- /dev/null +++ b/content/STL12.md @@ -0,0 +1,119 @@ +##Python标准库12 数学与随机数 (math包,random包) + + + + +我们已经在Python运算中看到Python最基本的数学运算功能。此外,math包补充了更多的函数。当然,如果想要更加高级的数学功能,可以考虑选择标准库之外的numpy和scipy项目,它们不但支持数组和矩阵运算,还有丰富的数学和物理方程可供使用。 + +此外,random包可以用来生成随机数。随机数不仅可以用于数学用途,还经常被嵌入到算法中,用以提高算法效率,并提高程序的安全性。 + + + +##math包 + +math包主要处理数学相关的运算。math包定义了两个常数: + +math.e # 自然常数e + +math.pi # 圆周率pi + + + +此外,math包还有各种运算函数 (下面函数的功能可以参考数学手册): + +math.ceil(x) # 对x向上取整,比如x=1.2,返回2 + +math.floor(x) # 对x向下取整,比如x=1.2,返回1 + +math.pow(x,y) # 指数运算,得到x的y次方 + +math.log(x) # 对数,默认基底为e。可以使用base参数,来改变对数的基地。比如math.log(100,base=10) + +math.sqrt(x) # 平方根 + + + +三角函数: math.sin(x), math.cos(x), math.tan(x), math.asin(x), math.acos(x), math.atan(x) + +这些函数都接收一个弧度(radian)为单位的x作为参数。 + + + +角度和弧度互换: math.degrees(x), math.radians(x) + + + +双曲函数: math.sinh(x), math.cosh(x), math.tanh(x), math.asinh(x), math.acosh(x), math.atanh(x) + + + +特殊函数: math.erf(x), math.gamma(x) + + + +##random包 + +如果你已经了解伪随机数(psudo-random number)的原理,那么你可以使用如下: + +random.seed(x) + +来改变随机数生成器的种子seed。如果你不了解其原理,你不必特别去设定seed,Python会帮你选择seed。 + + + +1) 随机挑选和排序 + +random.choice(seq) # 从序列的元素中随机挑选一个元素,比如random.choice(range(10)),从0到9中随机挑选一个整数。 + +random.sample(seq,k) # 从序列中随机挑选k个元素 + +random.shuffle(seq) # 将序列的所有元素随机排序 + + + +2)随机生成实数 + +下面生成的实数符合均匀分布(uniform distribution),意味着某个范围内的每个数字出现的概率相等: + +random.random() # 随机生成下一个实数,它在[0,1)范围内。 + +random.uniform(a,b) # 随机生成下一个实数,它在[a,b]范围内。 + + + +下面生成的实数符合其它的分布 (你可以参考一些统计方面的书籍来了解这些分布): + +random.gauss(mu,sigma) # 随机生成符合高斯分布的随机数,mu,sigma为高斯分布的两个参数。 + +random.expovariate(lambd) # 随机生成符合指数分布的随机数,lambd为指数分布的参数。 + +此外还有对数分布,正态分布,Pareto分布,Weibull分布,可参考下面链接: + +http://docs.python.org/library/random.html + + + +假设我们有一群人参加舞蹈比赛,为了公平起见,我们要随机排列他们的出场顺序。我们下面利用random包实现: + +import random +all_people = ['Tom', 'Vivian', 'Paul', 'Liya', 'Manu', 'Daniel', 'Shawn'] +random.shuffle(all_people) +for i,name in enumerate(all_people): + print(i,':'+name) + + +##练习 + +设计下面两种彩票号码生成器: + +1. 从1到22中随机抽取5个整数 (这5个数字不重复) + +2. 随机产生一个8位数字,每位数字都可以是1到6中的任意一个整数。 + + + +##总结 + +math.floor(), math.sqrt(), math.sin(), math.degrees() + +random.random(), random.choice(), random.shuffle() diff --git a/content/intermediate07.md b/content/intermediate07.md new file mode 100644 index 0000000..43c5f49 --- /dev/null +++ b/content/intermediate07.md @@ -0,0 +1,114 @@ +#Python进阶07 函数对象 + + + + + +秉承着一切皆对象的理念,我们再次回头来看函数(function)。函数也是一个对象,具有属性(可以使用dir()查询)。作为对象,它还可以赋值给其它对象名,或者作为参数传递。 + + + +##lambda函数 + +在展开之前,我们先提一下lambda函数。可以利用lambda函数的语法,定义函数。lambda例子如下: +```python +func = lambda x,y: x + y +print func(3,4) +``` +lambda生成一个函数对象。该函数参数为x,y,返回值为x+y。函数对象赋给func。func的调用与正常函数无异。 + + + +以上定义可以写成以下形式: +```python +def func(x, y): + return x + y +``` + +##函数作为参数传递 + +函数可以作为一个对象,进行参数传递。函数名(比如func)即该对象。比如说: +```python +def test(f, a, b): + print 'test' + print f(a, b) + +test(func, 3, 5) +``` +test函数的第一个参数f就是一个函数对象。将func传递给f,test中的f()就拥有了func()的功能。 + + + +我们因此可以提高程序的灵活性。可以使用上面的test函数,带入不同的函数参数。比如: +```python +test((lambda x,y: x**2 + y), 6, 9) +``` + +##map()函数 + +map()是Python的内置函数。它的第一个参数是一个函数对象。 +```python +re = map((lambda x: x+3),[1,3,5,6]) +``` +这里,map()有两个参数,一个是lambda所定义的函数对象,一个是包含有多个元素的表。map()的功能是将函数对象依次作用于表的每一个元素,每次作用的结果储存于返回的表re中。map通过读入的函数(这里是lambda函数)来操作数据(这里“数据”是表中的每一个元素,“操作”是对每个数据加3)。 + +在Python 3.X中,map()的返回值是一个循环对象。可以利用list()函数,将该循环对象转换成表。 + + + +如果作为参数的函数对象有多个参数,可使用下面的方式,向map()传递函数参数的多个参数: +```python +re = map((lambda x,y: x+y),[1,2,3],[6,7,9]) +``` +map()将每次从两个表中分别取出一个元素,带入lambda所定义的函数。 + + + +##filter()函数 + +filter函数的第一个参数也是一个函数对象。它也是将作为参数的函数对象作用于多个元素。如果函数对象返回的是True,则该次的元素被储存于返回的表中。filter通过读入的函数来筛选数据。同样,在Python 3.X中,filter返回的不是表,而是循环对象。 + + + +filter函数的使用如下例: + +```python +def func(a): + if a > 100: + return True + else: + return False + +print filter(func,[10,56,101,500]) +``` + + +##reduce()函数 + +reduce函数的第一个参数也是函数,但有一个要求,就是这个函数自身能接收两个参数。reduce可以累进地将函数作用于各个参数。如下例: +```python +print reduce((lambda x,y: x+y),[1,2,5,7,9]) +``` +reduce的第一个参数是lambda函数,它接收两个参数x,y, 返回x+y。 + +reduce将表中的前两个元素(1和2)传递给lambda函数,得到3。该返回值(3)将作为lambda函数的第一个参数,而表中的下一个元素(5)作为lambda函数的第二个参数,进行下一次的对lambda函数的调用,得到8。依次调用lambda函数,每次lambda函数的第一个参数是上一次运算结果,而第二个参数为表中的下一个元素,直到表中没有剩余元素。 + +上面例子,相当于(((1+2)+5)+7)+9 + + + +根据mmufhy的提醒: reduce()函数在3.0里面不能直接用的,它被定义在了functools包里面,需要引入包,见评论区。 + + + +##总结 + +函数是一个对象 + +用lambda定义函数 + +map() + +filter() + +reduce() diff --git a/content/intermediate08.md b/content/intermediate08.md new file mode 100644 index 0000000..8c449de --- /dev/null +++ b/content/intermediate08.md @@ -0,0 +1,127 @@ +#Python进阶08 异常处理 + + + + +##异常处理 + +在项目开发中,异常处理是不可或缺的。异常处理帮助人们debug,通过更加丰富的信息,让人们更容易找到bug的所在。异常处理还可以提高程序的容错性。 + +我们之前在讲循环对象的时候,曾提到一个StopIteration的异常,该异常是在循环对象穷尽所有元素时的报错。 + +我们以它为例,来说明基本的异常处理。 + +一个包含异常的程序: +```python +re = iter(range(5)) + +for i in range(100): + print re.next() + +print 'HaHaHaHa' +``` +首先,我们定义了一个循环对象re,该循环对象将进行5次循环,每次使用序列的一个元素。 + +在随后的for循环中,我们手工调用next()函数。当循环进行到第6次的时候,re.next()不会再返回元素,而是抛出(raise)StopIteration的异常。整个程序将会中断。 + + + +我们可以修改以上异常程序,直到完美的没有bug。但另一方面,如果我们在写程序的时候,知道这里可能犯错以及可能的犯错类型,我们可以针对该异常类型定义好”应急预案“。 +```python +re = iter(range(5)) + +try: + for i in range(100): + print re.next() +except StopIteration: + print 'here is end ',i + +print 'HaHaHaHa' +``` +在try程序段中,我们放入容易犯错的部分。我们可以跟上except,来说明如果在try部分的语句发生StopIteration时,程序该做的事情。如果没有发生异常,则except部分被跳过。 + +随后,程序将继续运行,而不是彻底中断。 + + + +完整的语法结构如下: + +```python +try: + ... +except exception1: + ... +except exception2: + ... +except: + ... +else: + ... +finally: + ... +``` + + +如果try中有异常发生时,将执行异常的归属,执行except。异常层层比较,看是否是exception1, exception2...,直到找到其归属,执行相应的except中的语句。如果except后面没有任何参数,那么表示所有的exception都交给这段程序处理。比如: +```python +try: + print(a*2) +except TypeError: + print("TypeError") +except: + print("Not Type Error & Error noted") +``` +由于a没有定义,所以是NameError。异常最终被except:部分的程序捕捉。 + + + +如果无法将异常交给合适的对象,异常将继续向上层抛出,直到被捕捉或者造成主程序报错。比如下面的程序 + +```python +def test_func(): + try: + m = 1/0 + except NameError: + print("Catch NameError in the sub-function") + +try: + test_func() +except ZeroDivisionError: + print("Catch error in the main program") +``` +子程序的try...except...结构无法处理相应的除以0的错误,所以错误被抛给上层的主程序。 + + + +如果try中没有异常,那么except部分将跳过,执行else中的语句。 + +finally是无论是否有异常,最后都要做的一些事情。 + +流程如下, + +try->异常->except->finally + +try->无异常->else->finally + + + +##抛出异常 + +我们也可以自己写一个抛出异常的例子: +```python +print 'Lalala' +raise StopIteration +print 'Hahaha' +``` +这个例子不具备任何实际意义。只是为了说明raise语句的作用。 + +StopIteration是一个类。抛出异常时,会自动有一个中间环节,就是生成StopIteration的一个对象。Python实际上抛出的,是这个对象。当然,也可以自行声场对象: +```python +raise StopIteration() +``` + +##总结 +```python +try: ... except exception: ... else: ... finally: ... +raise exception +``` diff --git a/content/intermediate09.md b/content/intermediate09.md new file mode 100644 index 0000000..32c0fa3 --- /dev/null +++ b/content/intermediate09.md @@ -0,0 +1,110 @@ +#Python进阶09 动态类型 + + + + +动态类型(dynamic typing)是Python另一个重要的核心概念。我们之前说过,Python的变量(variable)不需要声明,而在赋值时,变量可以重新赋值为任意值。这些都与动态类型的概念相关。 + + + +##动态类型 + +在我们接触的对象中,有一类特殊的对象,是用于存储数据的。常见的该类对象包括各种数字,字符串,表,词典。在C语言中,我们称这样一些数据结构为变量。而在Python中,这些是对象。 + +对象是储存在内存中的实体。但我们并不能直接接触到该对象。我们在程序中写的对象名,只是指向这一对象的引用(reference)。 + + + +引用和对象分离,是动态类型的核心。引用可以随时指向一个新的对象: +```python +a = 3 +a = 'at' +``` +第一个语句中,3是储存在内存中的一个整数对象。通过赋值,引用a指向对象3。 + +第二个语句中,内存中建立对象‘at’,是一个字符串(string)。引用a指向了'at'。此时,对象3不再有引用指向它。Python会自动将没有引用指向的对象销毁(destruct),释放相应内存。 + +(对于小的整数和短字符串,Python会缓存这些对象,而不是频繁的建立和销毁。) + + +```python +a = 5 +b = a +a = a + 2 +``` +再看这个例子。通过前两个句子,我们让a,b指向同一个整数对象5(b = a的含义是让引用b指向引用a所指的那一个对象)。但第三个句子实际上对引用a重新赋值,让a指向一个新的对象7。此时a,b分别指向不同的对象。我们看到,即使是多个引用指向同一个对象,如果一个引用值发生变化,那么实际上是让这个引用指向一个新的引用,并不影响其他的引用的指向。从效果上看,就是各个引用各自独立,互不影响。 + + + +其它数据对象也是如此: +```python +L1 = [1,2,3] +L2 = L1 +L1 = 1 +``` + +但注意以下情况 +```python +L1 = [1,2,3] +L2 = L1 +L1[0] = 10 +print L2 +``` +在该情况下,我们不再对L1这一引用赋值,而是对L1所指向的表的元素赋值。结果是,L2也同时发生变化。 + +原因何在呢?因为L1,L2的指向没有发生变化,依然指向那个表。表实际上是包含了多个引用的对象(每个引用是一个元素,比如L1[0],L1[1]..., 每个引用指向一个对象,比如1,2,3), 。而L1[0] = 10这一赋值操作,并不是改变L1的指向,而是对L1[0], 也就是表对象的一部份(一个元素),进行操作,所以所有指向该对象的引用都受到影响。 + +(与之形成对比的是,我们之前的赋值操作都没有对对象自身发生作用,只是改变引用指向。) + + + +列表可以通过引用其元素,改变对象自身(in-place change)。这种对象类型,称为可变数据对象(mutable object),词典也是这样的数据类型。 + +而像之前的数字和字符串,不能改变对象本身,只能改变引用的指向,称为不可变数据对象(immutable object)。 + +我们之前学的元组(tuple),尽管可以调用引用元素,但不可以赋值,因此不能改变对象自身,所以也算是immutable object. + + + +##从动态类型看函数的参数传递 + +函数的参数传递,本质上传递的是引用。比如说: + +```python +def f(x): + x = 100 + print x + +a = 1 +f(a) +print a +``` + +参数x是一个新的引用,指向a所指的对象。如果参数是不可变(immutable)的对象,a和x引用之间相互独立。对参数x的操作不会影响引用a。这样的传递类似于C语言中的值传递。 + + + +如果传递的是可变(mutable)的对象,那么改变函数参数,有可能改变原对象。所有指向原对象的引用都会受影响,编程的时候要对此问题留心。比如说: + +```python +def f(x): + x[0] = 100 + print x + +a = [1,2,3] +f(a) +print a +``` + + +动态类型是Python的核心机制之一。可以在应用中慢慢熟悉。 + + + +##总结 + +引用和对象的分离,对象是内存中储存数据的实体,引用指向对象。 + +可变对象,不可变对象 + +函数值传递 diff --git "a/\345\206\205\345\256\271/Python\346\240\207\345\207\206\345\272\223\342\200\224\342\200\224\350\265\260\351\251\254\350\247\202\350\212\261" "b/\345\206\205\345\256\271/Python\346\240\207\345\207\206\345\272\223\342\200\224\342\200\224\350\265\260\351\251\254\350\247\202\350\212\261" new file mode 100644 index 0000000..e4bc4a8 --- /dev/null +++ "b/\345\206\205\345\256\271/Python\346\240\207\345\207\206\345\272\223\342\200\224\342\200\224\350\265\260\351\251\254\350\247\202\350\212\261" @@ -0,0 +1,119 @@ +#Python标准库——走马观花 + + + + + +Python的一大好处在于它有一套很有用的标准库(standard library)。标准库是随着Python一起安装在你的电脑中的,是Python的一部分 (当然也有特殊情况。有些场合会因为系统安全性的要求,不使用全部的标准库,比如说Google App Engine)。 + + + +利用已有的类(class)和函数(function)进行开发,可以省去你从头写所有程序的苦恼。这些标准库就是盖房子已经烧好的砖,要比你自己去烧砖来得便捷得多。 + + + +我将根据我个人的使用经验中,先挑选出标准库下面三个方面的包(package)介绍,以说明标准库的强大功能: + +Python增强 + +系统互动 + +网络 + + + +##第一类:Python增强 + +Python自身的已有的一些功能可以随着标准库的使用而得到增强。 + +1) 文字处理 + +Python的string类提供了对字符串进行处理的方法。但Python并不止步于此。通过标准库中的re包,Python实现了对正则表达式(regular expression)的支持。Python的正则表达式可以和Perl以及Linux bash的正则表达相媲美。 + +(正则表达式通过自定义的模板在文本中搜索或替换符合该模板的字符串。比如你可以搜索一个文本中所有的数字。正则表达式的关键在于根据自己的需要构成模板。) + +此外,Python标准库还为字符串的输出提供更加丰富的格式, 比如: string包,textwrap包。 + + + +2) 数据对象 + +我们之前的快速教程介绍了表(list), 字典(dictionary)等数据对象。它们各自有不同的特征,适用于不同场合的对数据的组织和管理。Python的标准库定义了更多的数据对象,比如说数组(array),队列(Queue)。这些数据对象也分别有各自的特点和功能。一个熟悉数据结构(data structure)的Python用户可以在这些包中找到自己需要的数据结构。 + +此外,我们也会经常使用copy包,以复制对象。 + + + +3) 日期和时间 + +日期和时间的管理并不复杂,但容易犯错。Python的标准库中对日期和时间的管理颇为完善(利用time包管理时间,利用datetime包管理日期和时间),你不仅可以进行日期时间的查询和变换(比如:2012年7月18日对应的是星期几),还可以对日期时间进行运算(比如2000.1.1 13:00的378小时之后是什么日期,什么时间)。通过这些标准库,还可以根据需要控制日期时间输出的文本格式(比如:输出’2012-7-18‘还是'18 Jul 2012') + + + +4) 数学运算 + +标准库中,Python定义了一些新的数字类型(decimal包, fractions包), 以弥补之前的数字类型(integer, float)可能的不足。标准库还包含了random包,用于处理随机数相关的功能(产生随机数,随机取样等)。math包补充了一些重要的数学常数和数学函数,比如pi,三角函数等等。 + +(尽管numpy并不是标准库中的包,但它的数组运算的良好支持,让它在基于Python的科研和计算方面得到相当广泛的应用,可以适当关注。) + + + +5) 存储 + +之前我们的快速教程中,只提及了文本的输入和输出。实际上,Python可以输入或输出任意的对象。这些对象可以通过标准库中的pickle包转换成为二进制格式(binary),然后存储于文件之中,也可以反向从二进制文件中读取对象。 + +此外,标准库中还支持基本的数据库功能(sqlite3包)。XML和csv格式的文件也有相应的处理包。 + +   + +##第二类:系统互动 + +系统互动,主要指Python和操作系统(operate system)、文件系统(file system)的互动。Python可以实现一个操作系统的许多功能。它能够像bash脚本那样管理操作系统,这也是Python有时被成为脚本语言的原因。 + + + +1) Python运行控制 + +sys包被用于管理Python自身的运行环境。Python是一个解释器(interpreter), 也是一个运行在操作系统上的程序。我们可以用sys包来控制这一程序运行的许多参数,比如说Python运行所能占据的内存和CPU, Python所要扫描的路径等。另一个重要功能是和Python自己的命令行互动,从命令行读取命令和参数。 + + + +2) 操作系统 + +如果说Python构成了一个小的世界,那么操作系统就是包围这个小世界的大世界。Python与操作系统的互动可以让Python在自己的小世界里管理整个大世界。 + +os包是Python与操作系统的接口。我们可以用os包来实现操作系统的许多功能,比如管理系统进程,改变当前路径(相当于’cd‘),改变文件权限等,建立。但要注意,os包是建立在操作系统的平台上的,许多功能在Windows系统上是无法实现的。另外,在使用os包中,要注意其中的有些功能已经被其他的包取代。 + +我们通过文件系统来管理磁盘上储存的文件。查找、删除,复制文件,以及列出文件列表等都是常见的文件操作。这些功能经常可以在操作系统中看到(比如ls, mv, cp等Linux命令),但现在可以通过Python标准库中的glob包、shutil包、os.path包、以及os包的一些函数等,在Python内部实现。 + +subprocess包被用于执行外部命令,其功能相当于我们在操作系统的命令行中输入命令以执行,比如常见的系统命令'ls'或者'cd',还可以是任意可以在命令行中执行的程序。 + + + +4) 线程与进程 + +Python支持多线程(threading包)运行和多进程(multiprocessing包)运行。通过多线程和多进程,可以提高系统资源的利用率,提高计算机的处理速度。Python在这些包中,附带有相关的通信和内存管理工具。此外,Python还支持类似于UNIX的signal系统,以实现进程之间的粗糙的信号通信。 + + + +##第三类:网络 + +现在,网络功能的强弱很大程度上决定了一个语言的成功与否。从Ruby, JavaScript, php身上都可以感受到这一点。Python的标准库对互联网开发的支持并不充分,这也是Django等基于Python的项目的出发点: 增强Python在网络方面的应用功能。这些项目取得了很大的成功,也是许多人愿意来学习Python的一大原因。但应注意到,这些基于Python的项目也是建立在Python标准库的基础上的。 + + + +1) 基于socket层的网络应用 + +socket是网络可编程部分的底层。通过socket包,我们可以直接管理socket,比如说将socket赋予给某个端口(port),连接远程端口,以及通过连接传输数据。我们也可以利用SocketServer包更方便地建立服务器。 + +通过与多线程和多进程配合,建立多线程或者多进程的服务器,可以有效提高服务器的工作能力。此外,通过asyncore包实现异步处理,也是改善服务器性能的一个方案。 + + + +2) 互联网应用 + +在实际应用中,网络的很多底层细节(比如socket)都是被高层的协议隐藏起来的。建立在socket之上的http协议实际上更容易也更经常被使用。http通过request/responce的模式建立连接并进行通信,其信息内容也更容易理解。Python标准库中有http的服务器端和客户端的应用支持(BaseHTTPServer包; urllib包, urllib2包), 并且可以通过urlparse包对URL(URL实际上说明了网络资源所在的位置)进行理解和操作。 + + + +以上的介绍比较粗糙,只希望能为大家提供一个了解标准库的入口。欢迎大家一起分享标准库的使用经验。