python中的一点小知识与库(4)

目录
12. atexit模块
13. time模块
14. types 模块
15. gc 模块
16. fileinput 模块
17. shutil 模块
18. tempfile 模块
19. StringIO ,cStringIO 模块
20. mmap 模块
21. with关键字
22. yield关键字
23. threading模块
24. muliprocesing模块
25. Collections模块

十二、 atexit模块

atexit模块很简单,只定义了一个register函数用于注册程序退出时的回调函数,我们可以在这个回调函数中做一些资源清理的操作。
注:如果程序是非正常crash,或者通过os._exit()退出,注册的回调函数将不会被调用。

我们也可以通过sys.exitfunc来注册回调,但通过它只能注册一个回调,而且还不支持参数。所以建议大家使用atexit来注册回调函数。但千万不要在程序中同时使用这两种方式,否则通过atexit注册的回调可能不会被正常调用。其实通过查阅atexit的源码,你会发现原来它内部是通过sys.exitfunc来实现的,它先把注册的回调函数放到一个列表中,当程序退出的时候,按先进后出的顺序调用注册的回调。如果回调函数在执行过程中抛出了异常,atexit会打印异常的文字信息,并继续执行下一下回调,直到所有的回调都执行完毕,它会重新抛出最后接收到的异常。

示例:

import atexit

def exit0(*args, **kwarg):
    print('exit0')
    for arg in args:
        print(' ' * 4, arg)

    for item in kwarg.items():
        print(' ' * 4, item)

def exit1():
    print('exit1')
    raise Exception('exit1')

def exit2():
    print('exit2')    

atexit.register(exit0, *[1, 2, 3], **{ "a": 1, "b": 2, })
atexit.register(exit1)
atexit.register(exit2)

@atexit.register
def exit3():
    print('exit3')

if __name__ == '__main__':
    pass

输出:回调函数执行的顺序与它们被注册的顺序刚才相反。

exit3
exit2
exit1
exit0
     1
     2
     3
     ('a', 1)
     ('b', 2)
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "D:\python project-neon\pythonTest\src\Sample.py", line 13, in exit1
    raise Exception('exit1')
Exception: exit1

十三、time模块

time 模块提供了一些处理日期和一天内时间的函数. 它是建立在 C 运行时库的简单封装. 给定的日期和时间可以被表示为浮点型(从参考时间, 通常是 1970.1.1 到现在经过的秒数. 即 Unix 格式), 或者一个表示时间的 struct (类元组).

1.获取当前时间

import time

print(time.time())
print(time.localtime())
print(time.gmtime())#格林时间,0时区

输出:

1494840919.9609907
time.struct_time(tm_year=2017, tm_mon=5, tm_mday=15, tm_hour=17, tm_min=35, tm_sec=19, tm_wday=0, tm_yday=135, tm_isdst=0)
time.struct_time(tm_year=2017, tm_mon=5, tm_mday=15, tm_hour=9, tm_min=35, tm_sec=19, tm_wday=0, tm_yday=135, tm_isdst=0)

2.将时间值转换为字符串

import time
now = time.localtime(time.time()) 
print(time.asctime(now) )#convert to a string
print(time.strftime("%y/%m/%d %H:%M", now)) #根据给定的格式,将time转化成string
print(time.strftime("%a %b %d", now) )

year, month, day, hour, minute, second, weekday, yearday, daylight = now 
print("%04d-%02d-%02d" % (year, month, day) )

输出:

Mon May 15 17:38:24 2017
17/05/15 17:38
Mon May 15
2017-05-15

3.将字符串转换为时间对象

在一些平台上, time 模块包含了 strptime 函数, 它的作用与 strftime 相反. 给定一个字符串和模式, 它返回相应的时间对象

import time
# make sure we have a strptime function! 
try: 
    strptime = time.strptime 
except AttributeError: 
    from time import strptime 

print(strptime("30 Nov 00", "%d %b %y") )
print(strptime("1 Jan 70 1:30pm", "%d %b %y %I:%M%p"))
time.struct_time(tm_year=2000, tm_mon=11, tm_mday=30, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=335, tm_isdst=-1)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=13, tm_min=30, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)

注意:转化的时间如果本身不合理,如2月31,则会抛出异常:ValueError: day is out of range for month。
另外:只有在系统的 C 库提供了相应的函数的时候, time.strptime 函数才可以使用. 对于没有提供标准实现的平台

4.Timing 相关

time 模块可以计算 Python 程序的执行时间, 如 Example 1-85 所示. 你可以测量 “wall time” (real world time), 或是”进程时间” (消耗的 CPU 时间).

并不是所有的系统都能测量真实的进程时间. 一些系统中(包括 Windows ), clock 函数通常测量从程序启动到测量时的 wall time. 进程时间的精度受限制. 在一些系统中, 它超过 30 分钟后进程会被清理. (原文: On many systems, it wraps around after just over 30 minutes.)
另参见 timing 模块( Windows 下的朋友不用忙活了,没有地~), 它可以测量两个事件之间的 wall time.

import time 

def procedure(): 
    time.sleep(2.5) 

# measure process time 
t0 = time.clock() 
procedure() 
print(time.clock() - t0, "seconds process time" )

# measure wall time 
t0 = time.time() 
procedure() 
print(time.time() - t0, "seconds wall time" )

输出:

2.5104586929245016 seconds process time
2.512604236602783 seconds wall time

十四、types 模块

types 模块包含了标准解释器定义的所有类型的类型对象,包括NoneType, TypeType, IntType, FloatType, BooleanType, BufferType, BuiltinFunctionType, BuiltinMethodType, ClassType, CodeType, ComplexType, DictProxyType, DictType, DictionaryType等。你可以使用 is 来检查一个对象是不是属于某个给定类型. (参看元类)

types 模块在第一次引入的时候会破坏当前的异常状态. 也就是说, 不要在异
常处理语句块中导入该模块 (或其他会导入它的模块 ) .

Python内建函数type(object),用于返回当前object对象的类型

十五、gc 模块

gc 模块提供了到内建循环垃圾收集器的接口.
Python 使用引用记数来跟踪什么时候销毁一个对象; 一个对象的最后一个引用一旦消失, 这个对象就会被销毁. (可使用sys模块的getrefcount查看对象的引用计数)

你可以使用 gc.collect 函数来强制完整收集. 这个函数将返回收集器销毁的对象的数量.

Python 的 gc 有比较强的功能,比如设置 gc.set_debug(gc.DEBUG_LEAK) 就可以进行循环引用导致的内存泄露的检查。如果在开发时进行内存泄露检查;在发布时能够确保不会内存泄露,那么就可以延长 Python 的垃圾回收时间间隔、甚至主动关闭垃圾回收机制,从而提高运行效率。

十六、fileinput 模块

fileinput模块可以对一个或多个文件中的内容进行迭代、遍历等操作。该模块input()函数有点类似文件readlines()方法,区别在于前者是一个迭代对象,需要用for循环迭代,后者是一次性读取所有行。

用fileinput对文件进行循环遍历,格式化输出,查找、替换等操作,非常方便。

常用函数

fileinput.input()       #返回能够用于for循环遍历的对象
fileinput.filename()    #返回当前文件的名称
fileinput.lineno()      #返回当前已经读取的行的数量(或者序号)
fileinput.filelineno()  #返回当前读取的行的行号
fileinput.isfirstline() #检查当前行是否是文件的第一行
fileinput.isstdin()     #判断最后一行是否从stdin中读取
fileinput.close()       #关闭队列

1. input函数

函数格式:fileinput.input (files=None, inplace=False, backup=”, bufsize=0, mode=’r’, openhook=None)

import fileinput
for line in fileinput.input('Sample2.py'):
    print(line)

输出:

#Sample2.py

print("hello world")

2. 文件替换

文本文件的替换操作很简单. 只需要把 inplace 关键字参数设置为 1 , 传递
给 input 函数, 该模块会帮你做好一切

import fileinput
for line in fileinput.input('Sample2.py',1):
    print(line.rstrip() +" #replace")

Sample2.py文件内容变成:

#Sample2.py #replace
print("hello world") #replace

十七、shutil 模块

shutil 实用模块包含了一些用于复制文件和文件夹的函数
主要函数:

1. shutil.copyfileobj(fsrc, fdst[, length]) :将文件内容拷贝到另一个文件中
2. shutil.copyfile(src, dst):拷贝文件
3. shutil.copymode(src, dst) :仅拷贝权限。内容、组、用户均不变。即dst需要存在,只是使用src文件的权限复写dst文件的权限,相当于chmod命令。
4. shutil.copystat(src, dst) :仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
5. shutil.copy(src, dst):拷贝文件和权限
6. shutil.copy2(src, dst) :拷贝文件和状态信息
7. shutil.copytree(src, dst, symlinks=False, ignore=None):递归的去拷贝文件夹
8. shutil.rmtree(path[, ignore_errors[, onerror]]) :递归的去删除文件
9. shutil.move(src, dst) :递归的去移动文件,它类似mv命令,其实就是重命名。
10. shutil.make_archive(base_name, format,...) :创建压缩包并返回文件路径,例如:zip、tar

使用示例:

import os
import shutil

shutil.copyfile("Sample2.py","Sample3.py") #拷贝一个文件,新文件和源文件一样

shutil.copymode("Sample2.py","Sample4.py")#

结果:
new出来了一个文件Sample4.py,内容和Sample2.py一样

十八、tempfile 模块

tempfile 模块允许你快速地创建名称唯一的临时文件供使用.

import tempfile 

file = tempfile.TemporaryFile() 
for i in range(100): 
    file.write("*" * 100) 

file.close() # removes the file! 

TemporaryFile 函数会自动挑选合适的文件名, 并打开文件, 。 而且它会确保该文件在关闭的时候会被删除. (在 Unix 下, 你可以删除一个已打开的文件, 这时文件关闭时它会被自动删除. 在其他平台上, 这通过一个特殊的封装类实现.)

十九、StringIO ,cStringIO 模块

它实现了一个工作在内存的文件对象 (内存文件). 在大多需要标准文件对象的地方都可以使用它来替换(应用StringIO有个好处,他的有些接口和文件操作是一致的,也就是说用同样的代码,可以同时当成文件操作或者StringIO操作)
使用例子:使用 StringIO 模块从内存文件读入内容, getvalue 方法用来返回它内部的字符串值。

import io

MESSAGE = "That man is depriving a village somewhere of a computer scientist." 

file = io.StringIO(MESSAGE) 

print(file.read() )
print(file.getvalue())

输出:

That man is depriving a village somewhere of a computer scientist.
That man is depriving a village somewhere of a computer scientist.

另外,StringIO 可以用于重新定向 Python 解释器的输出,如sys.stdout = StringIO.StringIO()

cStringIO 是一个可选的模块, 是 StringIO 的c语言版的实现,更快速。 它的工作方式和 StringIO 基本相同, 但是它不可以被继承,且cStringIO没有len和pos属性。(还有,cStringIO不支持Unicode编码)

注意:

在python 3中,cStringIO 和StringIO都移到io模块中了,在上面的例子上体现出来了,需要导入io包。

二十、mmap 模块

https://docs.python.org/2/library/mmap.html

该模块提供对内存文件操作的支持。

内存映射文件对象行为和普通的string,file对象行为类似。但是和string不同的是,内存映射文件对象是可变的。创建内存映射文件对象,然后你可使用re模块进行搜索,同时由于对象是可变的,可以使用索引进行修改,如obj[index] =’a’,或是使用切片操作。

内存映射文件对象通过mmap构造(具体的操作参数在windows和Unix系统上是不同的),但都需要提供一个文件描述符(如果文件对象已经存在了,可通过fileno函数获取正确的fileno参数)

注意:如果你想创建一个可写,缓存的内存映射对象,你首先应该对该文件调用一次flush函数,保证之前缓存的修改也被映射反映到当前的内存中了。

Windows环境下,mmap.map使用方法:mmap.mmap(fileno, length[, tagname[, access[, offset]]] ),映射fileno文件句柄指定的文件的length字节到内存中,并创建一个mmap对象。(length不能为空,否则抛出异常,在windows下不支持空映射。length > size(file),则扩充文件;length=0,则 映射大小=size(file))

Unix版本:class mmap.mmap(fileno, length[, flags[, prot[, access[, offset]]]])

Memory-mapped file objects 支持的方法:
(1)close() :关闭映射对象。之后不能再对该对象调用其他方法
(2)find(string[,star[,end]]) :索引,返回最小索引号(如果找到的话).在python 3之后,需要参数为byte-like object.
(3)flush([offset, size]) : 刷会磁盘
(4)move(dest, src, count) :
(5)read(num) :
(6)read_byte() :
(7)readline() :
(8)resize(newsize) :
(9)rfind(string[, start[, end]]) :返回最大的索引号
(10)seek(pos[, whence]) :
(11)size() :返回文件大小(可比内存映射区大)
(12)tell() :返回文件指针当前位置
(13)write(string),write_byte(byte) :

例子:

import mmap 
import re

file = open("Sample2.py", "r+") 
data =mmap.mmap(file.fileno(), 0) 

# search 
index = data.find(b"replace") 
print(index, repr(data[index-5:index+15]) )

# regular expressions work too! 
m = re.search(b"replace", data) 
print(m.start(), m.group() )

输出:

13 b'.py #replace\r\nprint('
13 b'replace'

另外,可使用mmap.map()创建一片匿名内存区(通过fileno参数=-1,即可)
例如:

import mmap
import os

mm = mmap.mmap(-1, 13)
mm.write("Hello world!")

总结:感觉StringIO 和mmap使用上有些相似之处。StringIO是创建内存中的String对象,但是该对象支持的接口类似于文件。而mmap是将文件映射到内存中。两种都是通过使用内存,提高速度。

二一、with关键字

原文地址:https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/

with 语句是在 Python 2.5 版本引入的,从 2.6 版本开始成为缺省的功能。with 语句作为try/finally 编码范式的一种替代,用于对资源访问进行控制的场合。本章对 with 语句进行介绍,包括 with 语句的基本语法和工作原理,自定义支持 with 语句的类,以及使用 contextlib 工具加入对 with 语句的支持,使读者更好的了解和使用 with 语句。

1.术语

要使用 with 语句,首先要明白上下文管理器这一概念。有了上下文管理器,with 语句才能工作。
下面是一组与上下文管理器和with 语句有关的概念。
(1)上下文管理协议(Context Management Protocol):包含方法 enter() 和exit(),支持该协议的对象要实现这两个方法。
(2)上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了enter() 和 exit() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,也可以通过直接调用其方法来使用。
(3)运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 enter() 和exit() 方法实现,enter() 方法在语句体执行之前进入运行时上下文,exit() 在语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。
(4)上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象。
(5)语句体(with-body):with 语句包裹起来的代码块,在执行语句体之前会调用上下文管理器的 enter() 方法,执行完语句体之后会执行 exit() 方法。

2.基本语法和工作原理

with 语句的语法格式如下:

    with context_expression [as target(s)]:
        with-body

这里 context_expression 要返回一个上下文管理器对象,该对象并不赋值给 as 子句中的 target(s) ,如果指定了 as 子句的话,会将上下文管理器的 enter() 方法的返回值赋值给 target(s)。target(s) 可以是单个变量,或者由“()”括起来的元组(不能是仅仅由“,”分隔的变量列表,必须加“()”)。

Python 对一些内建对象进行改进,加入了对上下文管理器的支持,可以用于 with 语句中,比如可以自动关闭文件、线程锁的自动获取和释放等。假设要对一个文件进行操作,使用 with 语句可以有如下代码:

    with open(r'somefileName') as somefile:
        for line in somefile:
            print line
            # ...more code

这里使用了 with 语句,不管在处理文件过程中是否发生异常,都能保证 with 语句执行完毕后已经关闭了打开的文件句柄。如果使用传统的 try/finally 范式,则要使用类似如下代码:

    somefile = open(r'somefileName')
    try:
        for line in somefile:
            print line
            # ...more code
    finally:
        somefile.close()

比较起来,使用 with 语句可以减少编码量。已经加入对上下文管理协议支持的还有模块 threading、decimal 等。

PEP 0343(http://www.python.org/dev/peps/pep-0343/) 对 with 语句的实现进行了描述。with 语句的执行过程类似如下代码块:

   context_manager = context_expression
    exit = type(context_manager).__exit__  
    value = type(context_manager).__enter__(context_manager)
    exc = True   # True 表示正常执行,即便有异常也忽略;False 表示重新抛出异常,需要对异常进行处理
    try:
        try:
            target = value  # 如果使用了 as 子句
            with-body     # 执行 with-body
        except:
            # 执行过程中有异常发生
            exc = False
            # 如果 __exit__ 返回 True,则异常被忽略;如果返回 False,则重新抛出异常
            # 由外层代码对异常进行处理
            if not exit(context_manager, *sys.exc_info()):
                raise
    finally:
        # 正常退出,或者通过 statement-body 中的 break/continue/return 语句退出
        # 或者忽略异常退出
        if exc:
            exit(context_manager, None, None, None) 
        # 缺省返回 None,None 在布尔上下文中看做是 False

(1)执行 context_expression,生成上下文管理器 context_manager
(2)调用上下文管理器的 enter() 方法;如果使用了 as 子句,则将 enter() 方法的返回值赋值给 as 子句中的 target(s)
(3)执行语句体 with-body
(4)不管是否执行过程中是否发生了异常,执行上下文管理器的 exit() 方法,exit() 方法负责执行“清理”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用 exit(None, None, None) ;如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用 (5)exit(exc_type, exc_value, exc_traceback)
出现异常时,如果 exit(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理

3.自定义上下文管理器

开发人员可以自定义支持上下文管理协议的类。自定义的上下文管理器要实现上下文管理协议所需要的 enter() 和 exit() 两个方法:
(1)context_manager.enter() :进入上下文管理器的运行时上下文,在语句体执行前调用。with 语句将该方法的返回值赋值给 as 子句中的 target,如果指定了 as 子句的话
(2)context_manager.exit(exc_type, exc_value, exc_traceback) :退出与上下文管理器相关的运行时上下文,返回一个布尔值表示是否对发生的异常进行处理。参数表示引起退出操作的异常,如果退出时没有发生异常,则3个参数都为None。如果发生异常,返回
True 表示不处理异常,否则会在退出该方法后重新抛出异常以由 with 语句之外的代码逻辑进行处理。如果该方法内部产生异常,则会取代由 statement-body 中语句产生的异常。要处理异常时,不要显示重新抛出异常,即不能重新抛出通过参数传递进来的异常,只需要将返回值设置为 False 就可以了。之后,上下文管理代码会检测是否 exit() 失败来处理异常

下面通过一个简单的示例来演示如何构建自定义的上下文管理器。注意,上下文管理器必须同时提供 enter() 和 exit() 方法的定义,缺少任何一个都会导致 AttributeError;with 语句会先检查是否提供了 exit() 方法,然后检查是否定义了 enter() 方法。
假设有一个资源 DummyResource,这种资源需要在访问前先分配,使用完后再释放掉;分配操作可以放到 enter() 方法中,释放操作可以放到 exit() 方法中。简单起见,这里只通过打印语句来表明当前的操作,并没有实际的资源分配与释放。

  class DummyResource:
    def __init__(self, tag):
            self.tag = tag
            print 'Resource [%s]' % tag
        def __enter__(self):
            print '[Enter %s]: Allocate resource.' % self.tag
            return self   # 可以返回不同的对象
        def __exit__(self, exc_type, exc_value, exc_tb):
            print '[Exit %s]: Free resource.' % self.tag
            if exc_tb is None:
                print '[Exit %s]: Exited without exception.' % self.tag
            else:
                print '[Exit %s]: Exited with exception raised.' % self.tag
                return False   # 可以省略,缺省的None也是被看做是False

DummyResource 中的 enter() 返回的是自身的引用,这个引用可以赋值给 as 子句中的 target 变量;返回值的类型可以根据实际需要设置为不同的类型,不必是上下文管理器对象本身。
exit() 方法中对变量 exc_tb 进行检测,如果不为 None,表示发生了异常,返回 False 表示需要由外部代码逻辑对异常进行处理;注意到如果没有发生异常,缺省的返回值为 None,在布尔环境中也是被看做 False,但是由于没有异常发生,exit() 的三个参数都为 None,上下文管理代码可以检测这种情况,做正常处理。
下面在 with 语句中访问 DummyResource :

    with DummyResource('Normal'):
        print '[with-body] Run without exceptions.'

    with DummyResource('With-Exception'):
        print '[with-body] Run with exception.'
        raise Exception
        print '[with-body] Run with exception. Failed to finish statement-body!'

第1个 with 语句的执行结果如下:

    Resource [Normal]
    [Enter Normal]: Allocate resource.
    [with-body] Run without exceptions.
    [Exit Normal]: Free resource.
    [Exit Normal]: Exited without exception.

可以看到,正常执行时会先执行完语句体 with-body,然后执行 exit() 方法释放资源。
第2个 with 语句的执行结果如下:

  Resource [With-Exception]
    [Enter With-Exception]: Allocate resource.
    [with-body] Run with exception.
    [Exit With-Exception]: Free resource.
    [Exit With-Exception]: Exited with exception raised.

    Traceback (most recent call last):
      File "G:/demo", line 20, in <module>
       raise Exception
    Exception

可以看到,with-body 中发生异常时with-body 并没有执行完,但资源会保证被释放掉,同时产生的异常由 with 语句之外的代码逻辑来捕获处理。

可以自定义上下文管理器来对软件系统中的资源进行管理,比如数据库连接、共享资源的访问控制等。

3.contextlib 模块

contextlib 模块提供了3个对象:装饰器 contextmanager、函数 nested 和上下文管理器 closing。使用这些对象,可以对已有的生成器函数或者对象进行包装,加入对上下文管理协议的支持,避免了专门编写上下文管理器来支持 with 语句。

(1)装饰器 contextmanager
contextmanager 用于对生成器函数进行装饰,生成器函数被装饰以后,返回的是一个上下文管理器,其 enter() 和 exit() 方法由 contextmanager 负责提供,而不再是之前的迭代子。被装饰的生成器函数只能产生一个值,否则会导致异常 RuntimeError;产生的值会赋值给 as 子句中的 target,如果使用了 as 子句的话。

  from contextlib import contextmanager

    @contextmanager
    def demo():
        print '[Allocate resources]'
        print 'Code before yield-statement executes in __enter__'
        yield '*** contextmanager demo ***'
        print 'Code after yield-statement executes in __exit__'
        print '[Free resources]'

    with demo() as value:
        print 'Assigned Value: %s' % value

结果输出如下:

  [Allocate resources]
    Code before yield-statement executes in __enter__
    Assigned Value: *** contextmanager demo ***
    Code after yield-statement executes in __exit__
    [Free resources]

可以看到,生成器函数中 yield 之前的语句在 enter() 方法中执行,yield 之后的语句在 exit() 中执行,而 yield 产生的值赋给了 as 子句中的 value 变量。
需要注意的是,contextmanager 只是省略了 enter() / exit() 的编写,但并不负责实现资源的“获取”和“清理”工作;“获取”操作需要定义在 yield 语句之前,“清理”操作需要定义 yield 语句之后,这样 with 语句在执行 enter() / exit() 方法时会执行这些语句以获取/释放资源,即生成器函数中需要实现必要的逻辑控制,包括资源访问出现错误时抛出适当的异常。

(2)函数 nested
nested 可以将多个上下文管理器组织在一起,避免使用嵌套 with 语句。

 with nested(A(), B(), C()) as (X, Y, Z):
         # with-body code here

类似于:

    with A() as X:
        with B() as Y:
            with C() as Z:
                 # with-body code here

需要注意的是,发生异常后,如果某个上下文管理器的 exit() 方法对异常处理返回 False,则更外层的上下文管理器不会监测到异常。

(3)上下文管理器 closing

  class closing(object):
        # help doc here
        def __init__(self, thing):
            self.thing = thing
        def __enter__(self):
            return self.thing
        def __exit__(self, *exc_info):
            self.thing.close()

上下文管理器会将包装的对象赋值给 as 子句的 target 变量,同时保证打开的对象在 with-body 执行完后会关闭掉。closing 上下文管理器包装起来的对象必须提供 close() 方法的定义,否则执行时会报 AttributeError 错误。

    class ClosingDemo(object):
        def __init__(self):
            self.acquire()
        def acquire(self):
            print 'Acquire resources.'
        def free(self):
            print 'Clean up any resources acquired.'
        def close(self):
            self.free()

    with closing(ClosingDemo()):
        print 'Using resources'

结果输出如下:

    Acquire resources.
    Using resources
    Clean up any resources acquired.

closing 适用于提供了 close() 实现的对象,比如网络连接、数据库连接等,也可以在自定义类时通过接口 close() 来执行所需要的资源“清理”工作。

二二、yield关键字

原文地址:https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/

带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ?
我们先抛开 generator,以一个常见的编程题目来展示 yield 的概念。

1.如何生成斐波那契數列

斐波那契(Fibonacci)數列是一个非常简单的递归数列,除第一个和第二个数外,任意一个数都可由前两个数相加得到。用计算机程序输出斐波那契數列的前 N 个数是一个非常简单的问题,许多初学者都可以轻易写出如下函数:

 def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        print b 
        a, b = b, a + b 
        n = n + 1

执行 fab(5),我们可以得到如下输出:

 >>> fab(5) 
 1 
 1 
 2 
 3 
 5

结果没有问题,但有经验的开发者会指出,直接在 fab 函数中用 print 打印数字会导致该函数可复用性较差,因为 fab 函数返回 None,其他函数无法获得该函数生成的数列。

要提高 fab 函数的可复用性,最好不要直接打印出数列,而是返回一个 List。以下是 fab 函数改写后的第二个版本:

def fab(max): 
    n, a, b = 0, 0, 1 
    L = [] 
    while n < max: 
        L.append(b) 
        a, b = b, a + b 
        n = n + 1 
    return L

可以使用如下方式打印出 fab 函数返回的 List:

 >>> for n in fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

改写后的 fab 函数通过返回 List 能满足复用性的要求,但是更有经验的开发者会指出,该函数在运行中占用的内存会随着参数 max 的增大而增大,如果要控制内存占用,最好不要用 List来保存中间结果,而是通过 iterable 对象来迭代。例如,在 Python2.x 中,代码:

for i in range(1000): pass

会导致生成一个 1000 个元素的 List,而代码:

 for i in xrange(1000): pass

则不会生成一个 1000 个元素的 List,而是在每次迭代中返回下一个数值,内存空间占用很小。因为 xrange 不返回 List,而是返回一个 iterable 对象。

利用 iterable 我们可以把 fab 函数改写为一个支持 iterable 的 class,以下是第三个版本的 Fab:

 class Fab(object): 
    def __init__(self, max): 
        self.max = max 
        self.n, self.a, self.b = 0, 0, 1 

    def __iter__(self): 
        return self 

    def next(self): 
        if self.n < self.max: 
            r = self.b 
            self.a, self.b = self.b, self.a + self.b 
            self.n = self.n + 1 
            return r 
        raise StopIteration()

Fab 类通过 next() 不断返回数列的下一个数,内存占用始终为常数:

 >>> for n in Fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

然而,使用 class 改写的这个版本,代码远远没有第一版的 fab 函数来得简洁。如果我们想要保持第一版 fab 函数的简洁性,同时又要获得 iterable 的效果,yield 就派上用场了:

 def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b 
        # print b 
        a, b = b, a + b 
        n = n + 1 

'''

第四个版本的 fab 和第一版相比,仅仅把 print b 改为了 yield b,就在保持简洁性的同时获得了 iterable 的效果。
调用第四版的 fab 和第二版的 fab 完全一致:

 >>> for n in fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5

简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。

也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:

 >>> f = fab(5) 
 >>> f.next() 
 1 
 >>> f.next() 
 1 
 >>> f.next() 
 2 
 >>> f.next() 
 3 
 >>> f.next() 
 5 
 >>> f.next() 
 Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
 StopIteration

当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。

我们可以得出以下结论:
(1)一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。
(2)虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断:

 >>> from inspect import isgeneratorfunction 
 >>> isgeneratorfunction(fab) 
 True

要注意区分 fab 和 fab(5),fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别:

 >>> import types 
 >>> isinstance(fab, types.GeneratorType) 
 False 
 >>> isinstance(fab(5), types.GeneratorType) 
 True

fab 是无法迭代的,而 fab(5) 是可迭代的:

 >>> from collections import Iterable 
 >>> isinstance(fab, Iterable) 
 False 
 >>> isinstance(fab(5), Iterable) 
 True

每次调用 fab 函数都会生成一个新的 generator 实例,各实例互不影响

 >>> f1 = fab(3) 
 >>> f2 = fab(5) 
 >>> print 'f1:', f1.next() 
 f1: 1 
 >>> print 'f2:', f2.next() 
 f2: 1 
 >>> print 'f1:', f1.next() 
 f1: 1 
 >>> print 'f2:', f2.next() 
 f2: 1 
 >>> print 'f1:', f1.next() 
 f1: 2 
 >>> print 'f2:', f2.next() 
 f2: 2 
 >>> print 'f2:', f2.next() 
 f2: 3 
 >>> print 'f2:', f2.next() 
 f2: 5

2.return 的作用

在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

3. 另一个例子

另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取:

 def read_file(fpath): 
    BLOCK_SIZE = 1024 
    with open(fpath, 'rb') as f: 
        while True: 
            block = f.read(BLOCK_SIZE) 
            if block: 
                yield block 
            else: 
                return

以上仅仅简单介绍了 yield 的基本概念和用法,yield 在 Python 3 中还有更强大的用法,我们会在后续文章中讨论。
注:本文的代码均在 Python 2.7 中调试通过

二三、threading 模块

threading 模块为线程提供了一个高级接口。它源自 Java 的线程实现. 和低级的 thread 模块相同, 只有你在编译解释器时打开了线程支持才可以使用它 。
你只需要继承 Thread 类, 定义好 run 方法, 就可以创建一 个新的线程. 使用时首先创建该类的一个或多个实例, 然后调用 start 方法. 这样每个实例的 run 方法都会运行在它自己的线程里

import threading 
import time, random 

class Counter: 
    def __init__(self): 
        self.lock = threading.Lock() 
        self.value = 0 

    def increment(self): 
        self.lock.acquire() # critical section 
        self.value = value = self.value + 1 
        self.lock.release() 
        return value 

counter = Counter() 

class Worker(threading.Thread): 
    def run(self): 
        for i in range(10): 
            # pretend we're doing something that takes 10?00 ms 
            value = counter.increment() # increment global counter 
            time.sleep(random.randint(10, 100) / 1000.0) 
            print(self.getName(), "-- task", i, "finished", value )

for i in range(10): 
    Worker().start() # start a worker 

二四、muliprocesing模块

1.Queue

import threading 
from multiprocessing import Queue 
import time, random 

WORKERS = 2 

class Worker(threading.Thread): 

    def __init__(self, queue): 
        self.__queue = queue 
        threading.Thread.__init__(self) 

    def run(self): 
        while 1: 
            item = self.__queue.get() 
            if item is None: 
                break # reached end of queue 

            # pretend we're doing something that takes 10?00 ms 
            time.sleep(random.randint(10, 100) / 1000.0) 

            print("task", item, "finished" )

# 
# try it 

queue = Queue(0) 

for i in range(WORKERS): 
    Worker(queue).start() # start a worker 

for i in range(10): 
    queue.put(i) 

for i in range(WORKERS): 
        queue.put(None) # add end-of-queue markers 

2.

二五、Collections模块

1.UserDict,UserList,UserString

要用来给想继承这些类的用户,用户又需要自定义一些列表的行为,在构造时的行为和deepcopy类似。看一个UserList的例子。

import collections
import copy

list = [1, 2, 3, 4,[5.1,5.2]]
userlist = collections.UserList(list)
deepcopylist=copy.deepcopy(list)

newl = list

print("userlist type:",type(userlist),",value=",userlist)
print(list, newl)
print("deepcopylist type:",type(deepcopylist),",value=",deepcopylist)

del list[2]

print("userlist type:",type(userlist),",value=",userlist)
print(list, newl)
print("deepcopylist type:",type(deepcopylist),",value=",deepcopylist)

2.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值