第10章 充电时刻

10.1 模块

 math cmath

>>> import math
>>> math.sin(0)
0.0


10.1.1 模块是程序


cat hello.py

#!/usr/bin/env python
#coding=utf-8
print "Hello ,world"
>>> import math
>>> math.sin(0)
0.0
>>> import sys
>>> sys.path.append('/root/python') #或者sys.path.expanduser('~/python')
>>> import hello
Hello ,world


导入模块的时候会生成pyc文件,下次导入的时候会导入.pyc文件,导入模块主要用于定义,比如变量、函数、类等,模块如果被导入,就不会被再次导入

即使执行import命令,这样做,可以避免循环导入,除非使用reload(),如果已经通过模块的类创建了一个变量,reload函数对于这个已经定义好的变量不会重新定义

>>> reload(hello)
Hello ,world

但是一般不建议使用relaod


10.1.2  模块用于定义

 模块可以保持自己的作用域

python -m progname args 可以执行Python脚本 


 1、加载模块中定义函数

 cat hello2.py
 #!/usr/bin/env python
#coding=utf-8
def hello():
    print "Hello,world!"

 

 >>> import hello2

这意味着hello函数在模块的作用域内被定义了,可以通过下面的方式来访问

>>> hello2.hello()
Hello,world!


我们可以通过同样的方法来使用任何模块的全局作用域中定义的名称

为了让代码重用,请将它模块化,把定义都放在模块中,这样其他的地方也可以用


2.在模块中增加测试代码

# cat hello3.py
#!/usr/bin/env python
#coding=utf-8
def hello():
    print "Hello,world!"
# test
hello()
>>> import sys
>>> sys.path.append('/root/python')
>>> import hello3   #导入的时候程序就已经执行
Hello,world!
>>> hello3.hello()
Hello,world!



>>> __name__  #告知模块是作为程序运行还是导入模块

'__main__'


>>> hello3.__name__  #导入的模块,name为模块名

'hello3'




加入__name__=='__main__'的目的是让模块在被导入的时候不执行,如果把它作为程序运行,hello函数会被执行。


# cat hello4.py
#!/usr/bin/env python
#coding=utf-8
def hello():
    print "Hello,world!"
def test():
    hello()
if __name__=='__main__':test()


>>> import hello4
>>> hello4.hello()
Hello,world!

>>> hello4.test() #将测试代码放在test函数中,在其他的模块中也能被运行
Hello,world!


10.1.3  让你的模块可用

1、将模块放置在正确的位置

>>> import sys,pprint
>>> pprint.pprint(sys.path)  #格式化打印
['',
 '/usr/lib64/python27.zip',
 '/usr/lib64/python2.7',
 '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk',
 '/usr/lib64/python2.7/lib-old',
 '/usr/lib64/python2.7/lib-dynload',
 '/usr/lib64/python2.7/site-packages',
 '/usr/lib64/python2.7/site-packages/gtk-2.0',
 '/usr/lib/python2.7/site-packages',
 '/root/python']

 

 

 /usr/lib64/python2.7/site-packages 是最好的选择

 

2、告诉编译器去×××

a、编辑sys.path,但是这不是通用的方法


b、export PYTHONPATH=$PYTHONPATH:"/Library/Python/2.7/site-packages:{$PYTHONPATH}"  


c、路径配置文件 .pth 文件来实现

 Python 在遍历已知的库文件目录过程中,如果见到一个 .pth 文件,就会将文件中所记录的路径加入到 sys.path 设置中,于是 .pth 文件说指明的库也就可以被 Python 运行环境找到了,比如放到统一放到python的site-packages目录中。

 .pth 文件:import语句会被执行,#会被忽略

# pwd

/usr/lib64/python2.7/site-packages

 cat .pth

/root/python


10.1.4 包

包就是模块所在的目录,必须包含一个命名为__init__.py的文件



~/python

~/python/drawing/

~/python/drawing/__init__.py

~/python/drawing/colors.py

~/python/drawing/shapes.py

import drawing          #import the drawing package  __init__ 模块的内容是可以用的,但是colors和shapes模块不可用
import drawing.colors     #import the colors module      通过全名可用drawing.colors 
from drawing import shapes  #import the shapes module       通过段名 shapes可用

10.2 探究模块

10.2.1 模块中有什么


1、使用dir

 显示对象以及模块中所有的函数、类、和变量

 

>>> import copy
 
>>> [n for n in dir(copy) if not n.startswith('_')]
['Error', 'PyStringMap', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref']


tab键也可以实现,readline和rlcompleter模块可实现



2、__all__变量

__all__定义了模块的共有接口,它告诉解释器从模块导入的所有名字代表是什么含义

>>> copy.__all__
['Error', 'copy', 'deepcopy']

from copy import * 只能导入__all__变量中的函数,要导入PyStringMap的话,酒席显示的实现

import copy.PyStringMap

import * 语句默认将会输出模块中所有不以下划线开头的全局全称



10.2.2 用help获取帮助

help(copy)
Help on function copy in module copy:
copy(x)
    Shallow copy operation on arbitrary Python objects.
    
    See the module's __doc__ string for more info.
>>> copy.__doc__
"Shallow copy operation on arbitrary Python objects.\n\n    See the module's __doc__ string for more info.\n



10.2.3 文档

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.


库参考在线文档:https://docs.python.org/2/library/index.html


10.2.4 使用源代码

要了解模块,是不能脱离源代码的,阅读源代码,事实上是学习Python最好的方式--除了自己编写代码外


查看源代码的位置

方法一、检查sys.path

方法二:

>>> import copy
>>> print copy.__file__
/usr/lib64/python2.7/copy.pyc


注意:一些模块并不包含源代码,他们可能已经融入到解释器内了,或者可能使用C语言写成的


10.3标准库:一些最爱


10.3.1 sys


argv    命令行参数,包括脚本名称

exit[argv]   退出当前的程序,可选参数为给定的返回值或者错误信息

modules      映射模块名字到载入模块的字典

path     查找模块所在目录的目录名列表

platform   平台标识符

stdin  标准输入流

stdout  标准输出流

stderr  标准错误流

# reverseargs.py
import sys
args = sys.argv[1:]
args.reverse()
print ' '.join(args)
# python reverseargs.py this is a test
test a is this


10.3.2 os


os.path 还包含一些用于检查、构造、删除目录和文件的函数,以及一些处理路径的函数(os.path.split 和os.path.join让你可以忽略os.pathsep)

environ  对环境变量进行映射  os.environ['PYTHONPATH']

system(command)  在shell中执行操作系统命令   execv popen创建于程序连接的类文件  subprocess模块

sep    路径中的分隔符

pathsep 分隔路径的分隔符

lineseq  行分隔符

urandom(n) 返回n字节的加密强随机数据


os.system('/usr/bin/firefox')
os.system(r'c:\"Program Files"\"Mozilia Firefox"\firefox.exe')
os.startfile(r'c:\Program Files\Mozilia Firefox\firefox.exe') windows中特有的函数


import webbrowser

webbrowser.open('http://www.python.org')


10.3.3 fileinput

fileinput模块让你能够轻松变量文本文件的所有行

python some_scipt.py file1.txt file2.txt file3.txt

cat file.txt |python some_scipt.py



input([files[,inplace[,backup]]]) 便于便利多个输入流中的行

filename       返回当前文件的名称

lineno()        返回当前累计的行数

filelineno()    返回当前文件的行数

isfirstline()    检查当前行是否是文件的第一行

isstdin          检查最后一行是否来自sys.stdin

nextfile()       关闭当前文件,移到到下一个文件

close()          关闭序列



# numberlines.py
import fileinput
for line in fileinput.input(inplace=True):
    line = line.rstrip()
    num  = fileinput.lineno()
    print '%-40s # %2i' % (line, num)


# python numberline.py  numberline.py


# cat numberline.py 
#!/usr/bin/env python                    #  1
#coding=utf-8                            #  2
import fileinput                         #  3
                                         #  4
for line in fileinput.input(inplace=True): #  5
    line = line.rstrip()                 #  6
    num  = fileinput.lineno()            #  7
    print '%-40s # %2i' % (line, num)    #  8
                                         #  9
                                         # 10


 


10.3.4集合、堆和双列队列

 1、集合

 

 >>> set(range(10))
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

set是序列(或者其他可迭代对象)构建的

>>> set([0,1,2,3,0,1,2,3])  #有驱虫的功能
set([0, 1, 2, 3])
>>> set(['fee','fie','foe'])  #set无序
set(['foe', 'fee', 'fie'])

 

对set的操作,除了检查成员资格以外,还可以使用标准的集合函数,比如并集、交集 

>>> a=set([1,2,3])
>>> b=set([2,3,4])
>>> b.union(a)
set([1, 2, 3, 4])
>>> a|b
set([1, 2, 3, 4])
>>> c=a&b
>>> c
set([2, 3])
>>> c.issuperset(a)
False
>>> c>=a
False
>>> a.intersection(b)
set([2, 3])

>>> a&b
set([2, 3])
>>> a - b
set([1])
>>> a.symmetric_difference(b)
set([1, 4])

>>> a^b
set([1, 4])

>>> a.copy()
set([1, 2, 3])

>>> a.copy is a
False

>>> mySets=[]
>>> for i in range(10):
...   mySets.append(set(range(i,i+5)))
... 
>>> 

>>> reduce(set.union,mySets)
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])


 

forzenset

forzenset构造函数创建给定集合的副本

 

 2、堆

 堆是优先队列的一种,heapq

 

heappush(heap,x)  将x入堆 ,不能将它用于任何列表中

heappop(heap)      将堆中最小的元素弹出

heapify(heap)      将heap属性强制应用到任意一个列表

heapreplace(heap,x)   将堆中最小的元素弹出,同时将x入堆

nlargest(n,iter)   返回iter中第n大的元素

nsmallest(n,iter)  返回iter中第n小的元素



>>> from heapq import *
>>> from random import shuffle
>>> data=range(10)
>>> shuffle(data)
>>> heap=[]

>>> for n in data:
...   heappush(heap,n)
... 
>>> heap
[0, 2, 1, 5, 3, 9, 4, 8, 6, 7]
>>> heappush(heap,0.5)
>>> heap
[0, 0.5, 1, 5, 2, 9, 4, 8, 6, 7, 3]
>>> heappop(heap)
0
>>> heappop(heap)
0.5
>>> heappop(heap)
1
>>> heappop(heap)
2
>>> heappop(heap)
3
>>> heap=[5.8,0,3,6,7,9,1,4,2]
>>> heapify(heap)
>>> heap
[0, 2, 1, 4, 7, 9, 3, 5.8, 6]
>>> heapreplace(heap,0.5)
0
>>> heap
[0.5, 2, 1, 4, 7, 9, 3, 5.8, 6]
>>> heapreplace(heap,10)
0.5
>>> heap
[1, 2, 3, 4, 7, 9, 10, 5.8, 6]



双端队列(deque)


>>> from collections import deque
>>> q=deque(range(5))
>>> q.append(5)
>>> q.appendleft(6)
>>> q
deque([6, 0, 1, 2, 3, 4, 5])
>>> q.append(5)
>>> q
deque([6, 0, 1, 2, 3, 4, 5, 5])
>>> q.pop()
5
>>> q
deque([6, 0, 1, 2, 3, 4, 5])
>>> q.popleft()
6
>>> q
deque([0, 1, 2, 3, 4, 5])
>>> q.rotate(3)
>>> q
deque([3, 4, 5, 0, 1, 2])
>>> q.rotate(-1)
>>> q
deque([4, 5, 0, 1, 2, 3])



 

 

10.3.5 time

0   年

1   月

2   日

3   时

4   分

5   秒  范围为0-61

6  周

7  儒历日

8  夏令时


>>> import time
>>> time.asctime()
'Sun Oct 22 16:44:24 2017'


asctime([tuple]) 将时间元组转换为字符串

localtime([secs])  将秒数转换为时间元组

mktime(tuple)   将时间元组转换为本地时间

sleep(secs)    休眠

strptime(string,[,format])  将字符串解析为时间元组

time()           当前时间



相关模块 datetime  timeit 模块


10.3.6 random


相关函数 os.urandom 或者random.SystemRandom


random()                        返回0<=n<=1的随机数

getrandbits(n)                以长×××形式返回n个随机数

uniform(a,b)                    返回随机的实数n

randrange([start],stop,[step])  返回rang([start],stop,[step])中的随机数

choice(seq)                      从序列seq中随机返回任意元素

shuffle(seq[,random])     原地指定序列seq

sample(seq,n)                从seq中选择n个随机而且独立的元素


>>> import time
>>> time.asctime()
'Sun Oct 22 16:44:24 2017'
>>> 
>>> 
>>> 
>>> from random import *
>>> 
>>> from time import *
>>> date1=(2008,1,1,0,0,0,-1,-1,-1)
>>> time1=mktime(date1)
>>> time1
1199116800.0
>>> date2=(2009,1,1,0,0,0,-1,-1,-1)
>>> time2=mktime(date2)
>>> time2
1230739200.0
>>> random_time=uniform(time1,time2)
>>> random_time
1213927848.236082
>>> print asctime(localtime(random_time))
Fri Jun 20 10:10:48 2008



>>> from random import randrange
>>> num=input('How many dice?')
How many dice?6
>>> sides=input('How many sides per die?')
How many sides per die?6
>>> sum=0
>>> for i in range(num): sum+=randrange(sides)+1
... 
>>> print 'The result is',sum
The result is 47



# cat fortune.py 
#!/usr/bin/env python
#coding=utf-8
import fileinput,random
fortunes=list(fileinput.input())
print random.choice(fortunes)

# python fortune.py  fortune.py 
#coding=utf-8






>>> values=range(1,11)+'Jack Queen King'.split()
>>> suits='diamonds clubs hearts spades'.split()
>>> deck=['%s of %s' %(v,s) for v in values for s in suits]
>>> from pprint import pprint
>>> pprint(deck[:12])
['1 of diamonds',
 '1 of clubs',
 '1 of hearts',
 '1 of spades',
 '2 of diamonds',
 '2 of clubs',
 '2 of hearts',
 '2 of spades',
 '3 of diamonds',
 '3 of clubs',
 '3 of hearts',
 '3 of spades']
>>> from random import shuffle
>>> shuffle(deck)
>>> pprint(deck[:12])
['3 of hearts',
 '9 of clubs',
 '6 of hearts',
 '6 of clubs',
 'Jack of diamonds',
 '6 of diamonds',
 'Queen of spades',
 '4 of diamonds',
 '9 of spades',
 '8 of hearts',
 'King of hearts',
 '5 of spades']

 

 

>>> while deck: ignore=raw_input(deck.pop())
... 
9 of diamonds
3 of diamonds
9 of hearts
8 of diamonds
Queen of hearts
Queen of diamonds
1 of diamonds
2 of clubs




 

10.3.7 shelve 文件中存储数据

open函数,调用的时候,它会返回一个Shelf对象

1、潜在的陷阱


>>> import shelve
>>> 
>>> s=shelve.open('test.dat')
>>> s['x']=['a','b','c']
>>> s['x'].append('d')
>>> s['x']
['a', 'b', 'c']



解决方法:

方法一

>>> temp=s['x']
>>> temp.append('d')
>>> s['x']=temp
>>> s['x']
['a', 'b', 'c', 'd']


方法二:将open函数的writeback参数设置为true,这样数据结构会先保存在内存中,只有在关闭shelf的时候才写回磁盘



2、简单的数据库示例

# database.py
import sys, shelve
def store_person(db):
    """
    Query user for data and store it in the shelf object
    """
    pid = raw_input('Enter unique ID number: ')
    person = {}
    person['name']  = raw_input('Enter name: ')
    person['age']  = raw_input('Enter age: ')
    person['phone'] = raw_input('Enter phone number: ')
    db[pid] = person
def lookup_person(db):
    """
    Query user for ID and desired field, and fetch the corresponding data from
    the shelf object
    """
    pid = raw_input('Enter ID number: ')
    field = raw_input('What would you like to know? (name, age, phone)  ')
    field = field.strip().lower()
    print field.capitalize() + ':', \
          db[pid][field]
def print_help():
    print 'The available commands are:'
    print 'store  : Stores information about a person'
    print 'lookup : Looks up a person from ID number'
    print 'quit   : Save changes and exit'
    print '?      : Prints this message'
def enter_command():
    cmd = raw_input('Enter command (? for help): ')
    cmd = cmd.strip().lower()
    return cmd
def main():
    database = shelve.open('C:\\database.dat') # You may want to change this name
    try:
        while True:
            cmd = enter_command()
            if   cmd == 'store':
                store_person(database)
            elif cmd == 'lookup':
                lookup_person(database)
            elif cmd == '?':
                print_help()
            elif cmd == 'quit':
                return
    finally:
        database.close()
if __name__ == '__main__': main()



10.3.8 re

1、什么是正则表达式

 可以匹配文本片段的模式

  通配符.

  对特殊字符进行转义 python\\.org 或者r'python\.org'

  字符集 [pj]python  [a-z]  [azA-z0-9]    [^abc] 反转字符集,匹配任何除了a、b、c之外的字符

  在字符集中,特殊字符通常是没有必要的,如果脱字符出现在字符集的开头,你需要转移,] - 也需要转移

  选择符和子模式  p(ython|erl) 用括号括起来就是子模式

  可选项和重复子模式,在子模式后面加上问号,就变成了可选项

  r'(http://)?(www\.)?python\.org'  

  

  (pattern)?允许模式重复0次或者1次

   pattern)+ 允许模式重复1次或者多次

   pattern)*  允许模式重复0次或者多次

  (pattern){m,n}:允许模式重复m-n次

  

  字符串的开始和结尾

  ^ $

  

  

2、re模块的内容

  compile(pattern[,flags])    根据包含正则表达式的字符串创建模式对象

  search(pattern,string[,flags]      在字符串中寻找模式

  match(pattern,string[,flags])      在字符串的开始处匹配模式

  split(pattern,string[,maxsplit=0])  根据模式的匹配项来分割字符串

  findall(pattern,string)              列出字符串中模式的所有匹配项

  sub(pat,repl,string[,count=0])     将字符串所有pat的匹配项用repl替代

  escape(string)                      将字符串中所有特殊正则表达式字符转义



>>> import re
>>> some_text='alpha,beta,,,,,gamma dlta'
>>> re.split('[, ]+',some_text)
['alpha', 'beta', 'gamma', 'dlta']




>>> re.split('o(o)','foobar')  如果模式中包含小括号,那么括起来的字符组合会散布在分割后的子字符串中间

['f', 'o', 'bar']

>>> re.split('[, ]+',some_text,maxsplit=2)
['alpha', 'beta', 'gamma dlta']
>>> re.split('[, ]+',some_text,maxsplit=1)
['alpha', 'beta,,,,,gamma dlta']
>>> pat='[a-zA-Z]+'
>>> text='"Hm .... Err -- are you sure?""he said ,sounding insecure"'
>>> re.findall(pat,text)
['Hm', 'Err', 'are', 'you', 'sure', 'he', 'said', 'sounding', 'insecure']


>>> pat=r'[.?\-",]+'
>>> re.findall(pat,text)
['"', '....', '--', '?""', ',', '"']



>>> pat='{name}'
>>> text='Dear {name}...'
>>> re.sub(pat,'Mr.Gumby',text)
'Dear Mr.Gumby...'


>>> re.escape('www.python.org')
'www\\.python\\.org'
>>> re.escape('But where is the ambiguity?')
'But\\ where\\ is\\ the\\ ambiguity\\?'



  

  

  

  

3、匹配对象和组

'There (was a (wee) (cooper)) who (lived in Fyfe)'


0 There was a wee cooper who lived in Fyfe

1 was a wee cooper

2 wee

3 cooper

4 lived in Fyfe



group([group1,....]) 获取给定子模式组的匹配项,模式为组0

start([group])   返回给定组的匹配项的开始位置  模式为组0

end([group])    返回给定组的匹配项的结束位置

span([group])   返回一个组的开始和结束位置 模式为组0



>>> m=re.match(r'www\.(.*)\..{3}','www.python.org')
>>> m.group(1)
'python'
>>> m.start(1)
4
>>> m.end(1)
10
>>> m.span(1)
(4, 10)



4、作为替换的组号和函数

emphasis_pattern=r'\*([^\*]+)\*'

>>> emphasis_pattern=re.compile(r'''
...   \*  #Beginning emphasis tag -- an asterisk
...   (   #Beginning group for capturing phrase
...   [^\*]+ #Capture anything except asterisks
...   ) #end group
...   \*  #Ending emphasis tag
...   ''',re.VERBOSE)
>>> re.sub(emphasis_pattern,r'\1','Hello,*world*!')
'Hello,world!'


贪婪和非贪婪模式

重复运算符模式是贪婪模式,它会尽可能的多的匹配

>>> emphasis_pattern=r'\*(.+)\*'   #贪婪模式,尽可能多的匹配
>>> re.sub(emphasis_pattern,r'\1','*This* is *it*!')
'This* is *it!'



>>> emphasis_pattern=r'\*(.+?)\*'   #非贪婪模式,尽可能少的匹配
>>> re.sub(emphasis_pattern,r'\1','*This* is *it*!')
'This is it!'


将函数作为替换内容可以让替换功能变得更加强大。

5、找到Email的发信人


# find_sender.py
import fileinput, re
pat = re.compile('From: (.*) <.*?>$')
for line in fileinput.input():
    m = pat.match(line)
    if m: print m.group(1)
    
import fileinput,re
pat=re.compile(r'[a-z\-\.]+@[a-z\-\.]+',re.IGNORECASE)
addresses=set()
for line in fileinput.input():
   for address in pat.findall(line):
      addresses.add(address)
      
for address in sorted(addresses):
    print address


次问题也可以使用email模块

6、模板系统示例

思路:

1、可以使用正则表达式匹配自动,提取内容

2、可以使用eval计算字符串的值,提供包含作用域的字典

3、可以用exec执行字符串的赋值操作

4、可以使用re.sub将求值的结果替换为处理后的字符串


# templates.py
import fileinput, re
# Matches fields enclosed in square brackets:
field_pat = re.compile(r'\[(.+?)\]')
# We'll collect variables in this:
scope = {}
# This is used in re.sub:
def replacement(match):
    code = match.group(1)
    try:
        # If the field can be evaluated, return it:
        return str(eval(code, scope))
    except SyntaxError:
        # Otherwise, execute the assignment in the same scope...
        exec code in scope
        # ...and return an empty string:
        return ''
# Get all the text as a single string:
# (There are other ways of doing this; see Chapter 11)
lines = []
for line in fileinput.input():
    lines.append(line)
text = ''.join(lines)
# Substitute all the occurrences of the field pattern:
print field_pat.sub(replacement, text)


cat 1.txt

[x=2]
[y=3]
the sum of [x] and [y] is [x+y]



# python templates.py  1.txt 


the sum of 2 and 3 is 5


execfilel类执行文件,就可以使用正常的Python语法了

cat magus.txt
[name     = 'Magnus Lie Hetland' ]
[email    = '[email protected]'     ]
[language = 'python'             ]
cat template.txt

[import time]
Dear [name],
I would like to learn how to program. I hear you use
the [language] language a lot -- is it something I
should consider?
And, by the way, is [email] your correct email address?
Fooville, [time.asctime()]
Oscar Frozzbozz



# python templates.py  magus.txt  template.txt 

Dear Magnus Lie Hetland,
I would like to learn how to program. I hear you use
the python language a lot -- is it something I
should consider?
And, by the way, is [email protected] your correct email address?
Fooville, Sun Oct 22 21:28:20 2017


模板可以使用Template类 



10.3.9 其他有趣的标准模块

functools

difflib 

hashlib

csv

timeit、profile

datetime

itertools

logging

getopt和optparse

cmd