python基础篇14-模块

模块

模块(modules)的概念:

在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Python中,一个.py文件就称之为一个模块(Module)。

使用模块有什么好处?

最大的好处是大大提高了代码的可维护性。

其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。

所以,模块一共三种:

    • python标准库
    • 第三方模块
    • 应用程序自定义模块

另外,使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。

 

 模块导入的方法:

存在以下两个.py文件,都位于F:\\code\\day18路径下:

calculate.py

1 print('ok')
2 
3 x = 3
4 def add(x,y):
5     return x+y
6 
7 def sub(x,y):
8     return x-y

 

bin.py

1.import语句:

 1 import sys
 2 
 3 #搜索路径:
 4 print(sys.path)  #['F:\\code\\day18', 'F:\\code', 'E:\\soft_install\\python\\python3.5\\python35.zip', 'E:\\soft_install\\python\\python3.5\\DLLs', 'E:\\soft_install\\python\\python3.5\\lib', 'E:\\soft_install\\python\\python3.5', 'E:\\soft_install\\python\\python3.5\\lib\\site-packages']
 5 
 6 import calculate  #作用是解释器通过搜索路径找到calculate.py模块后,将calculate.py中所有代码解释完成后(即执行模块)赋值给calculate对象,此时calculate.py里的所有方法以及变量都要通过calculate对象来调用。
 7 print(calculate.add(1,2))  #调用calculate.py模块的add方法
 8 # 输出:
 9 # ok
10 # 3
11 
12 print(x)  #报错:NameError: name 'x' is not defined
13 print(calculate.x)  #3

2.from....import....语句:

1 from calculate import add  也可以只导入模块的部分方法,则模块中的其它方法将无法调用
2 print(add(1,2))  #调用calculate.py模块的add方法
3 # 输出:
4 # ok
5 # 3
6 
7 print(sub(1,2))  #报错,NameError: name 'sub' is not defined

3.from....import* 语句:

 1 from calculate import *  
 2 print(add(1,2)) #和1的区别是无需通过calculate.add()来调用函数
 3 print(sub(1,2))
 4 print(x)  
 5 
 6 # 输出:
 7 # ok
 8 # 3
 9 # -1
10 # 3

这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。

4.运行本质:

1 # import calculate
2 # from calculate import add 

无论1还是2,首先通过sys.path找到calculate.py,然后执行calculate.py(全部执行),区别是1会将calculate这个变量名加载到名字空间,而2只会将add这个变量名加载进来。

5.自定义方法名

1 from calculate import add as plus
2 
3 add(1,2)  #报错
4 plus(1,2) #这是需要通过plus来调用方法

模块导入流程:

1. 先从sys.modules里查看模块是否已经被导入

2. 模块如果没有被导入,就依据sys.path路径寻找模块

3. 如果在sys.path路径下找到模块就导入模块

4. 创建这个模块的命名空间,执行模块文件(.py文件),并把模块文件中的名字都放到该命名空间里。

 

包(package)

作用:如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。举个例子,一个abc.py的文件就是一个名字叫abc的模块,一个xyz.py的文件就是一个名字叫xyz的模块。现在,假设我们的abcxyz这两个模块名字与其他模块冲突了,于是我们可以通过包来组织模块,避免冲突;

注意:每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录(文件夹),而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是对应包的名字;调用包就是执行包下的__init__.py文件(即import PACKGE 即执行包下的__init__.py文件)

 

  • 存在如下目录结构:

 

 在bin.py模块中调用web下的logger模块:

1 from web import logger
2 logger.logger()   #在bin.py模块中就可以实现调用web下logger模块中的方法了

 

  •  存在如下目录结构:

 

在bin.py模块中调用web下web2下的logger模块:

1 from web.web2 mport logger
2 logger.logger()

只调用logger下的某些方法:

1 from web.web2.logger import logger  #调用logger模块下的logger方法
2 logger.logger()

 

  • BASE_DIR引入:

存在如下目录结构:

main.py:

1 #import logger   #如果这样写,在bin.py中调用main方法时会报无法找到logger的错误,应该改成如下的方式
2 from module import logger 
3 def main:
4     logger.logger()

 

 logger.py

1 def logger:
2     print('logger')

 

如果修改bin.py是程序的入口,在bin.py中如何调用main.py中的main函数?

bin.py

1 from module import main
2 main.main()  #这句话类似于将main方法中的所有代码复制至该模块(bin.py)下

注意:bin.py在pycharm中可以正常执行,因为在pycharm中在sys.path中将包bin的父目录的路径也添加到搜索路径,所以在pycharm中可以搜索到module;但是再命令行下将报错(因为在bin.py模块下无法找到module包)。

解决:

__file__:获取程序的相对路径,如

print(__file__)  #输出bin.py (在pycharm中打印显示时会将该相对路径转化为绝对路径,其他环境中还是相对路径)

print(os.path.abspath(__file__)) # C:\\Users\\Administrator\\PycharmProjects\\ATM\\bin  (根据相对路径找到绝对路径)

BASE_DIR=(os.path.abspath(os.path.abspath(__file__)))  #C:\\Users\\Administrator\\PycharmProjects\\ATM

优化后的bin.py的代码如下:将程序移植到任何环境下都能执行

1 import sys,os
2 BASE_DIR=(os.path.abspath(os.path.abspath(__file__))) 
3 sys.path.append(BASE_DIR)
4 
5 from module import main
6 
7 main.main()

 

if __name__ =='__main__':

如果我们是直接执行某个.py文件的时候,在该文件中”__name__ == '__main__'“是True,但是我们如果从另外一个.py文件通过import导入该文件的时候,这时__name__的值就是我们这个py文件的名字而不是__main__;这个功能还有一个用处:调试代码的时候,在”if __name__ == '__main__'“中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!

例子:

存在以下目录结构:

foo.py代码如下:功能模块

def hello:

    print('hello')

hello()   #调试代码,单独执行foo.py时会执行hello()方法,外部调用foo模块时也会执行hello()方法

bin.py代码如下:调用模块

import foo

foo.hello()

输出:输出了两次hello

hello

hello

优化:

foo.py代码如下:

def hello:

    print('hello')

#print(__main__)   在该模块下执行输出的是__main__,则if __name__='__main__'就为True,就会执行调试代码;在模块调用时结果为foo(即为模块名),则在模块调用时if __name__!='__main__',则就不会执行调试代码

if __name__='__main__':

    hello()   #调试代码,单独执行foo.py时会执行hello()方法,外部调用foo模块时将不会执行hello()方法

bin.py代码如下:调用模块

import foo

foo.hello()

输出:只输出一次hello

hello

 

time模块

 1 import time
 2 #print(help(time))  查看帮助
 3 
 4 print(time.time())    #1517193182.0534253   时间戳(s),unix诞生以来开始计算
 5 time.sleep(3)         #休眠3s
 6 print(time.clock())   #7.551609587825597e-07  计算cpu执行时间(不包括上面的3s)
 7 print(time.gmtime())  #结构化时间:time.struct_time(tm_year=2018, tm_mon=1, tm_mday=29, tm_hour=2, tm_min=36, tm_sec=5, tm_wday=0, tm_yday=29, tm_isdst=0) 即UTC(世界标准)时间,和北京时间差8h
 8 print(time.localtime())  #本地时间:time.struct_time(tm_year=2018, tm_mon=1, tm_mday=29, tm_hour=10, tm_min=45, tm_sec=10, tm_wday=0, tm_yday=29, tm_isdst=0)
 9 
10 #print(time.strftime(format,p_tuple))  将结构化时间以字符串时间形式输出
11 print(time.strftime("%Y-%m-%d %H:%M:%S" ))  #字符串时间即自定义格式输出日期 2018-01-29 10:55:02
12 struct_time=time.localtime()
13 print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))  #将结构化时间以字符串时间输出:2018-01-29 10:58:51
14 
15 #time.strptime(string,format)  将字符串时间以结构化时间输出
16 print(time.strptime("2018-01-29 10:58:51","%Y-%m-%d %H:%M:%S"))  #time.struct_time(tm_year=2018, tm_mon=1, tm_mday=29, tm_hour=10, tm_min=58, tm_sec=51, tm_wday=0, tm_yday=29, tm_isdst=-1)
17 #取某个时间值:
18 a=time.strptime("2018-01-29 10:58:51","%Y-%m-%d %H:%M:%S")
19 print(a.tm_hour)   #10
20 print(a.tm_mon)    #1
21 
22 #time.ctime(seconds)
23 print(time.ctime())  #取当前时间:Mon Jan 29 11:11:09 2018
24 print(time.ctime(234566))  #将给定的时间以看得懂的方式输出(unix诞生以来的时间开始计算)
25 
26 #time.mktime(p_tuple)
27 a=time.localtime()
28 print(time.mktime(a))  #将本地时间转化为时间戳:1517195833.0

几种时间格式之间的转换

 

#时间戳-->结构化时间
#time.gmtime(时间戳)    #UTC时间,与英国伦敦当地时间一致
#time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间 
>>>time.gmtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)
>>>time.localtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)

#结构化时间-->时间戳 
#time.mktime(结构化时间)
>>>time_tuple = time.localtime(1500000000)
>>>time.mktime(time_tuple)
1500000000.0
#结构化时间-->字符串时间
#time.strftime("格式定义","结构化时间")  结构化时间参数若不传,则显示当前时间
>>>time.strftime("%Y-%m-%d %X")
'2017-07-24 14:55:36'
>>>time.strftime("%Y-%m-%d",time.localtime(1500000000))
'2017-07-14'

#字符串时间-->结构化时间
#time.strptime(时间字符串,字符串对应格式)
>>>time.strptime("2017-03-16","%Y-%m-%d")
time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1)
>>>time.strptime("07/24/2017","%m/%d/%Y")
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)

 

datatime模块

1 import datetime
2 print(datetime.datetime.now())   #2018-01-29 11:20:48.342246

 

 

random模块-随机数模块

 1 import random
 2 print(random.random())  #取0-1内的随机数
 3 print(random.randint(1,8))  #1-8内的随机数,包括8
 4 print(random.choice("hello")) #在给定的字符串选取随机数
 5 print(random.choice([1,2,3,4,5])) #也可以放列表
 6 print(random.sample([1,2,[3,4]],2)) #在序列中随机选2个,[2, [3, 4]]
 7 print(random.randrange(1,10))  #取1-10的数,不包括10
 8 
 9 
10 #生成随机验证码
11 import random
12 checkcode = ''
13 for i in range(4):
14     current = random.randrange(0,4)
15     if current != i:
16         temp = chr(random.randint(65,90))
17     else:
18         temp = random.randint(0,9)
19     checkcode += str(temp)
20 print checkcode

 

 

os模块-和操作系统交互的模块

提供对操作系统进行调用的接口。

r:以字符原意思输出。

 1 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径;
 2 os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd ;os.chdir(r'F:\code')    # r:取消所有转义
 3 os.curdir  返回当前目录: ('.')
 4 os.pardir  获取当前目录的父目录字符串名:('..')
 5 os.makedirs('dirname1/dirname2')      可生成多层递归目录;os.makedirs(r'abc\lriwu\alen')
 6 os.removedirs('dirname1/dirname2')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推  ;s.removedirs(r'abc\lriwu\alen')
 7 os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
 8 os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
 9 os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
10 os.remove()  删除一个文件
11 os.rename("oldname","newname")  重命名文件/目录
12 os.stat('path/filename')  获取文件/目录信息    os.stat('path/filename').st_size   获取文件大小,返回值是int类型
13 os.sep    输出操作系统特定的路径分隔符,win下为"\",Linux下为"/"
14 os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
15 os.pathsep    输出用于分割文件路径的字符串,如环境变量;windows:';'  linux:':'
16 os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
17 os.system("bash command")  运行shell命令,直接显示,无返回值  os.system("dir")
ret = os.popen("dir").read() 运行shell命令,不显示,有返回值
print(ret)
18 os.environ 获取系统环境变量 19 os.path.abspath(path) 返回path规范化的绝对路径;print(os.path.abspath('./os.py')) # D:\code\os.py 20 os.path.split(path) 将path分割成目录和文件名二元组返回 21 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 22 os.path.basename(path) 返回path最后的文件名。如果path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 23 os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False 24 os.path.isabs(path) 如果path是绝对路径,返回True 25 os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False 26 os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False 27 os.path.join(path1[, path2[, ...]]) 根据当前操作系统的路径分隔符将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
print(os.path.join('c:','user','local')) # c:user\local
28 os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 29 os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
30 os.path.getsize(path) 返回path的大小,path为具体文件名,如果path为目录将不准确

注意:os.stat('path/filename')  获取文件/目录信息 的结构说明

stat 结构:

st_mode: inode 保护模式
st_ino: inode 节点号。
st_dev: inode 驻留的设备。
st_nlink: inode 的链接数。
st_uid: 所有者的用户ID。
st_gid: 所有者的组ID。
st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
st_atime: 上次访问的时间。
st_mtime: 最后一次修改的时间。
st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。
stat 结构

 

 

sys模块-和python解释器交互的模块

1 sys.argv           命令行参数List,第一个元素是程序本身文件名
2 sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
3 sys.version        获取Python解释程序的版本信息
4 sys.maxint         最大的Int值
5 sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
6 sys.platform       返回操作系统平台名称  print(sys.platform)  #win32
7 sys.stdout.write('please:')
8 val = sys.stdin.readline()[:-1]

 

 

hashlib模块

用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法;

 1 import hashlib
 2  
 3 m = hashlib.md5()
 4 m.update("Hello".encode('utf8')) #python3中,内存中的字符串都是unicode类型,update参数一定需要接收bytes类型,所以需要encode转换(因为python3中只有bytes和str两种数据类型)
 5 m.update("It's me".encode('utf8')) 
 6 print(m.digest()) #2进制格式hash   b']\xde\xb4{/\x92Z\xd0\xbf$\x9cR\xe3Br\x8a'
 7 print(len(m.hexdigest())) #16进制格式hash 5ddeb47b2f925ad0bf249c52e342728a
 8 
 9 
10 #加密过程等同于:
11 m2 = hashlib.md5()
12 m2.update("HelloIt's me".encode('utf8')) 
13 print(len(m2.hexdigest())) #16进制格式hash 5ddeb47b2f925ad0bf249c52e342728a
14 
15 
16 
17 
18 import hashlib
19  
20 # ######## md5 ########
21  
22 hash = hashlib.md5()
23 hash.update('admin')
24 print(hash.hexdigest())
25  
26 # ######## sha1 ########
27  
28 hash = hashlib.sha1()
29 hash.update('admin')
30 print(hash.hexdigest())
31  
32 # ######## sha256 ########
33  
34 hash = hashlib.sha256()
35 hash.update('admin')
36 print(hash.hexdigest())
37  
38  
39 # ######## sha384 ########
40  
41 hash = hashlib.sha384()
42 hash.update('admin')
43 print(hash.hexdigest())
44  
45 # ######## sha512 ########
46  
47 hash = hashlib.sha512()
48 hash.update('admin')
49 print(hash.hexdigest())

 

logging模块

1.简单应用

 1 import logging  
 2 logging.debug('debug message')  
 3 logging.info('info message')  
 4 logging.warning('warning message')  
 5 logging.error('error message')  
 6 logging.critical('critical message')  
 
 
 
 #输出
 WARNING:root:warning message
 ERROR:root:error message
 CRITICAL:root:critical message

 

 可见,默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),默认的日志格式为日志级别:Logger名称:用户输出消息。

 

2.灵活配置日志级别,日志格式,输出位置(文件输出和标准输出只能选一种)

 1 import logging  
 2 logging.basicConfig(level=logging.DEBUG,  
 3                     format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  
 4                     datefmt='%a, %d %b %Y %H:%M:%S',  
 5                     filename='/tmp/test.log',  
 6                     filemode='w')  
 7   
 8 logging.debug('debug message')  
 9 logging.info('info message')  
10 logging.warning('warning message')  
11 logging.error('error message')  
12 logging.critical('critical message')
 
 
 
 #查看输出:
 cat /tmp/test.log 
 Mon, 05 May 2014 16:29:53 test_logging.py[line:8] DEBUG debug message
 Mon, 05 May 2014 16:29:53 test_logging.py[line:9] INFO info message
 Mon, 05 May 2014 16:29:53 test_logging.py[line:10] WARNING warning message
 Mon, 05 May 2014 16:29:53 test_logging.py[line:11] ERROR error message
 Mon, 05 May 2014 16:29:53 test_logging.py[line:12] CRITICAL critical message

 

可见在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。(a:追加写;w:覆盖写)

  注意:如果没有指定filename和filemode,默认将日志打印到了标准输出中。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的默认日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open('test.log','w')),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息

 

3.logger对象

 上述几个例子中我们了解到了logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical() 分别用以记录不同级别的日志信息;logging.basicConfig() 用默认日志格式为日志系统建立一个默认的流处理器:设置基础配置(如日志级别等)并加到root logger中,这几个是logging模块级别的函数;另外还有一个模块级别的函数是logging.getLogger([name])(返回一个logger对象,如果没有指定名字将返回root logger)

先看一个最简单的过程:

 1 import logging
 2 #创建一个日志(logger)对象
 3 logger = logging.getLogger()
 4 
 5 # 创建一个handler即文件输出流对象,用于写入日志文件
 6 fh = logging.FileHandler('test.log')
 7 
 8 # 再创建一个handler即标准输出流对象,用于输出到控制台
 9 ch = logging.StreamHandler()
10 
11 #日志格式对象
12 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
13 
14 fh.setFormatter(formatter)  #为文件输出设置日志格式
15 ch.setFormatter(formatter)  #为标准输出设置日志格式
16 
17 #通过addHandler为logger添加文件输出对象和标准输出对象
18 logger.addHandler(fh) #logger对象可以添加多个fh和ch对象
19 logger.addHandler(ch)
20 #logger1.setLevel(logging.DEBUG)   设置输出日志级别,不指定默认是warnning
21 logger.debug('logger debug message')
22 logger.info('logger info message')
23 logger.warning('logger warning message')
24 logger.error('logger error message')
25 logger.critical('logger critical message')

 

输出:

#控制台输出
2018-01-30 09:53:10,887 - root - WARNING - logger warning message
2018-01-30 09:53:10,887 - root - ERROR - logger error message
2018-01-30 09:53:10,887 - root - CRITICAL - logger critical message



#文件输出
cat test.log
2018-01-30 09:53:10,887 - root - WARNING - logger warning message
2018-01-30 09:53:10,887 - root - ERROR - logger error message
2018-01-30 09:53:10,887 - root - CRITICAL - logger critical 

 

 流程图:

 

4.应用

 1 import os
 2 import time
 3 import logging
 4 from config import settings
 5 
 6 
 7 def get_logger(card_num, struct_time):
 8 
 9     if struct_time.tm_mday < 23:
10         file_name = "%s_%s_%d" %(struct_time.tm_year, struct_time.tm_mon, 22)
11     else:
12         file_name = "%s_%s_%d" %(struct_time.tm_year, struct_time.tm_mon+1, 22)
13 
14     file_handler = logging.FileHandler(
15         os.path.join(settings.USER_DIR_FOLDER, card_num, 'record', file_name),
16         encoding='utf-8'
17     )
18     fmt = logging.Formatter(fmt="%(asctime)s :  %(message)s")
19     file_handler.setFormatter(fmt)
20 
21     logger1 = logging.Logger('user_logger', level=logging.INFO)
22     logger1.addHandler(file_handler)
23     return logger1
View Code

 

 

ConfigParser模块

常用配置文件的格式如下:

 1 [DEFAULT]
 2 ServerAliveInterval = 45
 3 Compression = yes
 4 CompressionLevel = 9
 5 ForwardX11 = yes
 6   
 7 [bitbucket.org]
 8 User = hg
 9   
10 [topsecret.server.com]
11 Port = 50022
12 ForwardX11 = no

使用python生成一个这样的文档:

 1 import configparser
 2 
 3 config = configparser.ConfigParser()   #生成一个文件操作句柄
 4 config["DEFAULT"] = {'ServerAliveInterval': '45',
 5                      'Compression': 'yes',
 6                      'CompressionLevel': '9'}
 7 
 8 config['bitbucket.org'] = {}
 9 config['bitbucket.org']['User'] = 'hg'
10 config['topsecret.server.com'] = {}
11 topsecret = config['topsecret.server.com']
12 topsecret['Host Port'] = '50022'  # mutates the parser
13 topsecret['ForwardX11'] = 'no'  # same here
14 config['DEFAULT']['ForwardX11'] = 'yes'
15 with open('example.ini', 'w') as configfile:   #创建文件
16     config.write(configfile)

 

 

增删改查操作:

 1 import configparser
 2 
 3 config = configparser.ConfigParser()
 4 #---------------------------------------------查
 5 print(config.sections())   #[]
 6 config.read('example.ini')   #关联文件
 7 print(config.sections())    #['bitbucket.org', 'topsecret.server.com'] 即打印字段,default是默认字段,不会显示出来
 8 print(config.defaults())    #OrderedDict([('compression', 'yes'), ('compressionlevel', '9'), ('serveraliveinterval', '45'), ('forwardx11', 'yes')]) 查看default字段下的所有属性以及属性值
 9 print(config.items('bitbucket.org'))  #[('serveraliveinterval', '45'), ('compression', 'yes'), ('compressionlevel', '9'), ('forwardx11', 'yes'), ('user', 'hg')] 查看非default字段下的所有属性以及属性值
10 print(config.options('bitbucket.org'))#['user', 'serveraliveinterval', 'compression', 'compressionlevel', 'forwardx11'] 查看字段下的属性
11 print('bytebong.com' in config)# False   查看配置文件中是否有该字段
12 print(config.has_section('bitbucket.org'))  #查看配置文件中是否有该字段
13 print(config['bitbucket.org']['User']) # hg   查看属性值
14 
15 for key in config:
16     print(key)
17 # 输出:打印config下的所有字段
18 # DEFAULT
19 # bitbucket.org
20 # topsecret.server.com
21 
22 
23 for key in config['bitbucket.org']:
24     print(key)
25 # 输出:bitbucket.org字段下的属性以及default字段下的属性
26 # user
27 # compression
28 # compressionlevel
29 # serveraliveinterval
30 # forwardx11
31 
32 #---------------------------------------------删,改,增
33 
34 #删除字段
35 config.remove_section('topsecret.server.com')
36 
37 #添加字段
38 config.add_section('yuan')
39 
40 #修改属性值
41 config.set('bitbucket.org','user','lriwu')
42 
43 #删除键值对
44 config.remove_option('bitbucket.org','user')
45 
46 
47 (config.write(open('r.cfg', "w")))  #最后这句语句是必须的

 

 

re模块

正则表达式是一种小型的、高度专业化的编程语言,它内嵌在Python中,通过 re 模块实现。正则表达式模块被编译成一系列的字节码,然后由C 编写的匹配引擎执行。

作用:字符串提供的方法是完全匹配,无法实现模糊匹配,所以引入正则的原因是可以实现模糊匹配。

实现判断手机号码格式:

phone_number = input('please input your phone number : ')

while True:
    phone_number = input('please input your phone number : ')
    if len(phone_number) == 11 \
            and phone_number.isdigit()\
            and (phone_number.startswith('13') \
            or phone_number.startswith('14') \
            or phone_number.startswith('15') \
            or phone_number.startswith('18')):
        print('是合法的手机号码')
    else:
        print('不是合法的手机号码')

通过re模块:

import re
phone_number = input('please input your phone number : ')
if re.match('^(13|14|15|18)[0-9]{9}$',phone_number):
        print('是合法的手机号码')
else:
        print('不是合法的手机号码')

对比上面的两种写法,使用正则表达式会简便非常多。

正则表达式

正则表达式本身和python没有什么关系,就是匹配字符串内容的一种规则。

字符组:[]

在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。

单字符:

量词:

. ^ $

 

* + ? { }

注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

字符集[][^]

分组 ()与 或 |[^]

身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部🈶️数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对"\"进行转义,变成'\\'。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了。这个时候我们就用到了r'\n'这个概念,此时的正则是r'\\n'就可以了。( r的作用是取消‘\’的转义功能)

现在我们聊一聊\,先看下面两个匹配:

 1 #-----------------------------eg1:
 2 import re
 3 ret=re.findall('c\l','abc\le')
 4 print(ret)#[]   没有匹配
 5 ret=re.findall('c\\l','abc\le')
 6 print(ret)#[]   没有匹配
 7 ret=re.findall('c\\\\l','abc\le')    #在python解释器里先将‘\\\\’转义成‘\\’;再在re模块里将'\\'转义成‘\’ ,即就匹配到了‘c\l’
 8 print(ret)#['c\\l']   匹配到了
 9 ret=re.findall(r'c\\l','abc\le')     #r表示告诉python解释器里面的字符串就是原生字符串,在python接收器中无需转义
10 print(ret)#['c\\l']    匹配到了
11  
12 #-----------------------------eg2:
13 #之所以选择\b是因为\b在ASCII表中是有意义的
14 m = re.findall('\bblow', 'blow')
15 print(m)  #[]     没有匹配
16 m = re.findall(r'\bblow', 'blow')
17 print(m)  #['blow']   匹配到了

 

  

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配。

.*:尽可能取多次

几个常用的非贪婪匹配Pattern:

.*? 尽可能取少次

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

.*?的用法

. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
合在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现

flags有很多可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法

re.M(MULTILINE)多行模式,改变^和$的行为

re.S(DOTALL)点可以匹配任意字符,包括换行符

re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用

re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag

re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释

 

re模块

字符匹配(普通字符,元字符):

1.普通字符:字符串提供的方法是完全匹配,无法实现模糊匹配。

1 import re
2 re.findall('w\w{2}l', 'hello world')  #['worl']  模糊匹配
3 re.findall('alex','yuanaleSxalexwupeiqi') #['alex'] 精确匹配(如果只实现这一功能,使用字符串的方法就可以了)

 

2.元字符:可以实现模糊匹配

  • 元字符之. ^ $ * + ? { }
 1 import re
 2 
 3 #'.':匹配任意字符一次(不能匹配换行符)
 4 ret = re.findall('a..in', 'helloalvin')
 5 print(ret)  # ['alvin']
 6 
 7 #'^':行首匹配
 8 ret = re.findall('^a...n', 'alvinhelloawwwn')
 9 print(ret)  # ['alvin']
10 
11 #'$':行尾匹配
12 ret = re.findall('a...n$', 'alvinhelloawwwn')
13 print(ret)  # ['awwwn']
14 
15 #'*':即匹配前面的字符[0,+oo]次
16 ret = re.findall('abc*', 'abcccc')
17 print(ret)  # ['abcccc']   #贪婪匹配
18 ret = re.findall('abc*', 'ab')
19 print(ret)  # ['ab']   #匹配0次例子
20 
21 #'+':匹配前面的字符[1,+oo]次即匹配前面的字符至少一次
22 ret = re.findall('abc+', 'abccc')
23 print(ret)  # ['abccc']  #贪婪匹配
24 
25 #'?':匹配前面的字符[0,1]次
26 ret=re.findall('abc?','abccc')#[0,1]
27 print(ret)#['abc']  #贪婪匹配
28 ret = re.findall('a?b', 'aaaabhghabfb')
29 print(ret)  # ['ab','ab','b']
30 
31 
32 ret = re.findall('abc{1,4}', 'abccc')  #匹配前面的1-4次都行
33 print(ret)  # ['abccc']  贪婪匹配
34 ret = re.findall('a{5}b','aaaaab')  #aaaaab  匹配前面的5次

注意:前面的*,+,?等都是贪婪匹配,也就是尽可能多次匹配,后面加?号使其变成惰性匹配即按照最少的进行匹配;

1 ret=re.findall('abc*?','abcccccc')
2 print(ret)#['ab']

 

  • 元字符之字符集[]:
 1 # --------------------------------------------字符集[]
 2 ret = re.findall('a[bc]d', 'acd')   #或的关系[b,c]表示b或c
 3 print(ret)  # ['acd']
 4 
 5 ret = re.findall('[a-z]', 'acd')
 6 print(ret)  # ['a', 'c', 'd']
 7 
 8 #取消元字符的特殊功能
 9 ret = re.findall('[.*+]', 'a.cd+')  #这里的.*+只表示自身意义
10 print(ret)  # ['.', '+']
11 
12 # 在字符集里仍有功能的符号: - ^ \
13 ret = re.findall('[1-9]', '45dha3')     #'-'代表范围
14 print(ret)  # ['4', '5', '3']
15 
16 ret = re.findall('[^a,b]', '45bdha3')    #'^'代表取反即除了a和b以外的
17 #等同于:ret = re.findall('[^ab]', '45bdha3')
18 print(ret)  # ['4', '5', 'd', 'h', '3']
19 
20 
21 # 反斜杠后边跟元字符去除特殊功能,比如\.
22 # 反斜杠后边跟普通字符实现特殊功能,比如\d,相当于类 [0-9]
23 ret = re.findall('[\d]', '45bdha3')
24 print(ret)  # ['4', '5', '3']

反斜杠后边跟元字符去除特殊功能,比如\.

反斜杠后边跟普通字符实现特殊功能,比如\d

\d  匹配任何十进制数;它相当于类 [0-9]。
\D 匹配任何非数字字符;它相当于类 [^0-9]。
\s  匹配任何空白字符;它相当于类 [ \t\n\r\f\v]。
\S 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。
\w 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。
\W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]
\b  匹配一个特殊字符边界,比如空格 ,&,#,$等

1 ret=re.findall('I\b','I am LIST')
2 print(ret)#[]   
3 ret=re.findall(r'I\b','I am LI$T')
4 print(ret)#['I','I']

 

  • 元字符之分组()
1 print(re.search('(as)+','sdjkfasas').group())   #asas    '+' 匹配前面分组[1,+oo]次
2 ret = re.findall('www.(\w+).com','www.baidu.com') #['baidu'] 只会打印出组中的内容,分组是有优先的
3  ret = re.findall('www.(?:\w+).com','www.baidu.com') #['www.baidu.com'] 取消组的权限

 

1 ret=re.search('(?P<id>\d{2})','23/com')  #?P<id> 为分组取名字为id
2 print(ret.group())#23
3 print(ret.group('id'))#23  #通过分组名取匹配到的值

 注意:

1 findall的优先级查询:

import re

ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['oldboy']     这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可

ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['www.oldboy.com']

2 split的优先级查询

ret=re.split("\d+","eva3egon4yuan")
print(ret) #结果 : ['eva', 'egon', 'yuan']

ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan']

#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。

 

  • 元字符之或 | :或从左至右边匹配,只要匹配上就不继续匹配了,所以应该把长的放前面
ret=re.search('(?P<id>\d{2})/(?P<name>\w{3})','23/com')
 print(ret.group())#23/com     
 print(ret.group('id'))#23

 

  • re模块下的常用方法
 1 import re
 2 
 3 # 1
 4 re.findall('a', 'alvin yuan')  # ['a', 'a']即返回所有满足匹配条件的结果,放在列表里
 5 # 2
 6 ret = re.search('a', 'alvin yuan')  
7 print(ret) # <_sre.SRE_Match object; span=(0, 1), match='a'> 找整个字符串,遇到匹配的就返回,即返回匹配到的第一个对象,该对象可以调用group()返回结果;如果字符串没有匹配,则返回None,此时调用group()方法会报错。
8 print(ret.group()) # 'a'
ret = re.search('a', 'alvin yuan')
if ret:
print(ret.group())
8 # 3 9 re.match('a', 'abca').group() # 'a' 同search,不过只在字符串开始处进行匹配 10 re.match('a', 'df asbca as').group()  # 报错
11 # 4 分割 12 ret = re.split('b', 'abcd') 13 print(ret) # ['a', 'cd'] 14 15 ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割 16 print(ret) # ['', '', 'cd'] 17 18 ret = re.split('[js]', 'sdjksal') #['', 'd', 'k', 'al'] 19 20 # 5 替换 21 ret = re.sub('\d', 'abc', 'alvin5yuan6', 1) 22 print(ret) # alvinabcyuan6 23 ret = re.subn('\d', 'abc', 'alvin5yuan6') #没有指定第三个参数,全部替换 24 print(ret) # ('alvinabcyuanabc', 2) 25 26 # 6 compile可以把正则表达式编译成一个正则表达式对象,可以把经常使用的正则表达式编译成正则表达式对象,这样可以提高一定的效率。 27 obj = re.compile('\d{3}') #实现一种规则可以匹配多次 28 ret = obj.search('abc123eeee') 29 print(ret.group()) # 123
30
31 #7
32 ret = re.finditer('\d','ds3sy4784a')
33 print(res) #<callable_iterator object at 0x00000233DBFA5208> 返回一个存放匹配结果的迭代器对象
34 print(next(ret).group()) #3 查看第一个结果
35 print(next(ret).group())  #4   查看第二个结果

练习

import re


ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
#还可以在分组中利用?p<name>的形式给分组起名字,?P=tag_name:引用前面分组标签的名字
#获取的匹配结果可以直接用group('名字')拿到对应的值
print(ret.group('tag_name'))  #结果 :h1
print(ret.group())  #结果 :<h1>hello</h1>

ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
#如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
#获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())  #结果 :<h1>hello</h1>
匹配标签
import re

ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #['1', '2', '60', '40', '35', '5', '4', '3']

ret=re.findall(r"\d+\.\d+|\d+","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #['1', '2', '60', '40.35', '5', '4', '3']

ret=re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #['1', '2', '60', '', '5', '4', '3']
ret.remove('')
print(ret)  #['1', '2', '60', '5', '4', '3']


ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #['1', '-2', '60', '', '5', '-4', '3']
ret.remove("")
print(ret) #['1', '-2', '60', '5', '-4', '3']
匹配整数
1、 匹配一段文本中的每行的邮箱
      http://blog.csdn.net/make164492212/article/details/51656638

2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;

   分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
   一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$

3、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}

4、 匹配一个浮点数。       ^(-?\d+)(\.\d+)?$   或者  -?\d+\.?\d*

5、 匹配汉字。             ^[\u4e00-\u9fa5]{0,}$ 

6、 匹配出所有整数
匹配数字

 

json模块、pickle模块、shelve模块--序列化的模块

引入:无法将字典直接写入文件,需要通过eval()方法,不过,eval方法是有局限性的。对于普通的数据类型,json.loads和eval都能用,但遇到特殊类型的时候,eval就不管用了,所以eval的重点还是通常用来执行一个字符串表达式,并返回表达式的值。

 1 # 字典无法直接写入文件
 2 #dic = {'1':'111'}
 3 #f = open('test','w')
 4 #f.write(dic)  #报错 TypeError: write() argument must be str, not dict
 5 
 6 #需要将字典转化成字符串才能写入文件
 7 dic = {'1':'111'}
 8 str1 = str(dic)
 9 f = open('test','w')
10 f.write(str1)    #{'1': '111'} 成功写入文件
11 
12 #读取文件内容
13 # f = open('test','r')
14 # data = f.read()
15 # print(data['1'])   #在文件中保存的是字符串而不是字典,所以这种方式取值将会报错
16 
17 #通过eval转换成字典类型再取值
18 f = open('test','r')
19 data = f.read()
20 print(eval(data)['1'])  #111

序列化:

我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化即将数据类型转化成字符串的过程

序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化即将字符串转化成数据类型的过程

三种模块的区别:

如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。但是JSON也存在缺点:只有很少的一部分数据类型能够通过json转化成字符串。于是就有了pickle。

pickle:python中所有的数据类型都可以转化成字符串形式;但是pickle序列化的内容只有python能理解且部分反序列化依赖代码。

shelve:shelve模块比pickle模块简单,shelve只提供一个open方法,返回类似字典的对象,可读可写。key必须为字符串,而值可以是python所支持的数据类型。

在以下两种情况下才会使用到序列化

1.数据存储

2.网络传输

json:json是通用的序列化格式,能够做到不同语言之间的转换

JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:

序列化方法:dumps,直接对内存中的对象进行序列化操作,操作完成后对象依然在内存中存在

import json
dic={'name':'lriwu','age':'18'}
print(type(dic),dic)
data=json.dumps(dic)
print(type(data),data)
f=open('json_test','w')
f.write(data)
f.close()

# <class 'dict'> {'name': 'lriwu', 'age': '18'}
# <class 'str'> {"name": "lriwu", "age": "18"}

#文件中保存的内容:
#{"name": "lriwu", "age": "18"}   即以json对应的字典格式进行存储(json中对应的字典类型是{})

反序列化方法:loads,直接对内存中的对象进行序列化操作,操作完成后对象依然在内存中存在

1 import json
2 f=open('json_test','r')
3 # data=f.read()
4 # print(data) # {"age": "18", "name": "lriwu"}
5 # data['name'] #dumps序列化的无法直接取值,需要通过loads方法进行反序列化 6 data=f.read() 7 data=json.loads(data) 8 print(data['name']) #lriwu

 json可以对普通的数据类型转化成json的字符串;但是不能将高级的对象(函数或类)转化成json的字符串。

1 import json
2 
3 def foo():
4     print('ok')
5 
6 data=json.dumps(foo)  #TypeError: <function foo at 0x00000225799AC620> is not JSON serializable 函数不是json的序列化类型

json将普通的数据类型转化成json的字符串,只能是:数字、字符串、列表、字典、元组;但是不能将高级的对象(函数或类)转化成json的字符串,如果需要将这些高级的对象转化成jison的字符串,可以使用pickle。 

 

序列化方法:dump,向文件中写入。以下述为例:先将字典进行序列化操作成字符串后写入文件

import json
dic = {1:"a",2:'b'}
f = open('fff','w',encoding='utf-8')
json.dump(dic,f)
f.close()

# 文件中的内容
# {”1":"a","2":"b"}

反序列化方法:load,读文件中内容

import json
f = open('fff',encoding='utf-8')
ret = json.load(f)
f.close()
print(type(ret),ret)


# <class 'dict'> {'1':'a','2':'b'}

注意:dump(s)可以多次写入文件,当多次写入时,使用load(s)一次性读出来会报错即出现只能写无法读的现象;所以json只能在dump(s)一次性写入后,而后使用load(s)一次性读出。这又会产生另外一个问题:当写入文件的内容过大时,一次性读出非常占用内存。解决如下例子(使用loads和dumps):

# 分次写入文件
l = [{'k':'111'},{'k2':'111'},{'k3':'111'}]
f = open('file','w')
import json
for dic in l:
    str_dic = json.dumps(dic)
    f.write(str_dic+'\n')
f.close()


# file文件中内容如下:
{"k":"111"}
{"k2":"111"}
{"k3":"111"}



# 分次读文件内容
f = open('file')
import json
l = []
for line in f:
    dic = json.loads(line.strip())
    l.append(dic)
f.close()
print(l)

 # [{'k':'111'},{'k2':'111'},{'k3':'111'}]

 

pickle

序列化:dumps

 1 import pickle
 2 
 3 def foo():
 4     print('ok')
 5 
 6 data=pickle.dumps(foo)
 7 f=open('pickle_test','wb')  #pickle dumps时必须是以bytes数据类型写入,字符串类型无法写入;'wb':表示把写入文件的内容转化为bytes之后再写入文件;‘w’:默认写入的是str的数据类型(loads时也必须以bytes方式读)
 8 f.write(data)  
 9 f.close()
10 
11 #f=open('pickle_test','w') 将会报错TypeError: write() argument must be str, not bytes

 反序列化:loads

1 import pickle
2 def foo():
3     print('ok')
4 f=open('pickle_test','rb')
5 data=f.read()
6 data=pickle.loads(data)
7 data()  #调用data就相当于执行了foo()函数,前提是当前脚本也需要定义foo()函数即load和dumps两个文件都必须要有foo()函数

dump和load方法:

1 import json
2 dic = {'name':'lriwu','age':'18'}
3 f = open('JSON_test','w')
4 # data = json.dumps(dic)
5 # f.write(data)
6 json.dump(dic,f) #这句话等同于data = json.dumps(dic)和f.write(data)这两句即f.write(data)省略了
7 f.close()

 

1 import json
2 f=open('JSON_test','r')
3 # data=f.read()
4 # data=json.loads(data)
5 data = json.load(f)  #少了f.read()
6 print(data['name'])   #lriwu

注意:pickle可以实现分步写入,分步读出

import time
struct_time1  = time.localtime(1000000000)
struct_time2  = time.localtime(2000000000)
f = open('pickle_file','wb')
pickle.dump(struct_time1,f)   # 分步写入1
pickle.dump(struct_time2,f)   # 分步写入2
f.close()
f = open('pickle_file','rb')
struct_time1 = pickle.load(f)  # 分步读出1
struct_time2 = pickle.load(f)  # 分步读出2
print(struct_time1.tm_year)
print(struct_time2.tm_year)
f.close()

 

shelve模块

使用句柄直接操作数据类型。

存数据:

1 import shelve
2 f = shelve.open('shelve.txt')  #得到一个文件句柄
3  
4 f['stu1_info']={'name':'alex','age':'18'}   #后续还能继续添加其它字典,非常灵活
5 #print(f['stu1_info']) {'name':'alex','age':'18'}
7 #print(f['stu1_info']['age']) #18
8 #print(f.get('stu1_info')['age']) #18 取字典中的内容(本地取)

 取数据:(在另外一个文件中取上述存的数据)

import shelve
 
f = shelve.open('shelve.txt')

data = f.get('stu1_info')['age']
print(data)   #18

get方法:

 1 d = {'name':'lriwu','age':'18'}
 2 #取键值,方法一:
 3 print(d['name'])  #lriwu
 4 #print(d.['sex'])  #无该键将会报错
 5 
 6 
 7 #取键值,方法二:
 8 print(d.get('name')) #lriwu
 9 print(d.get('sex')) #None 无该键将会返回None
10 print(d.get('sex','male'))  #male 该键将会返回自定义的值

shelve模块有个限制,不支持多个应用同一时间往同一个DB进行写操作。所以当我们知道应用如果只进行读操作,我们可以让shelve通过只读方式打开DB。

import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}  #直接对文件句柄操作,就可以存入数据
f.close()

f1 = shelve.open('shelve_file')
existing = f1['key']  #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
f1.close()
print(existing)

# 只读方式:flag=r
f = shelve.open('shelve_file', flag='r')
existing = f['key']
f.close()
print(existing)
只读方式:flag=r

由于shelve在默认情况下是不会记录待持久化对象的任何修改的,所以我们在shelve.open()时需要修改默认参数,否则对象的修改不会保存。

import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}  #直接对文件句柄操作,就可以存入数据
f.close()


f1 = shelve.open('shelve_file')
print(f1['key'])  # {'string': 'Sample data', 'float': 9.5, 'int': 10}
f1['key']['new_value'] = 'this was not here before'
f1.close()

f2 = shelve.open('shelve_file')
print(f2['key'])  # {'float': 9.5, 'string': 'Sample data', 'int': 10}  现象是文件未被修改

# writeback=True
f2 = shelve.open('shelve_file', writeback=True)
print(f2['key'])  # {'float': 9.5, 'string': 'Sample data', 'int': 10}
f2['key']['new_value'] = 'this was not here before'
print(f2['key'])   # {'new_value': 'this was not here before', 'int': 10, 'string': 'Sample data', 'float': 9.5}
f2.close()
writeback=True

 

xml模块

xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。

xml的格式如下,就是通过<>节点来区别数据结构的:

 1 <?xml version="1.0"?>
 2 <data>
 3     <country name="Liechtenstein">
 4         <rank updated="yes">2</rank>
 5         <year>2008</year>
 6         <gdppc>141100</gdppc>
 7         <neighbor name="Austria" direction="E"/>
 8         <neighbor name="Switzerland" direction="W"/>
 9     </country>
10     <country name="Singapore">
11         <rank updated="yes">5</rank>
12         <year>2011</year>
13         <gdppc>59900</gdppc>
14         <neighbor name="Malaysia" direction="N"/>
15     </country>
16     <country name="Panama">
17         <rank updated="yes">69</rank>
18         <year>2011</year>
19         <gdppc>13600</gdppc>
20         <neighbor name="Costa Rica" direction="W"/>
21         <neighbor name="Colombia" direction="E"/>
22     </country>
23 </data>

xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:

 1 import xml.etree.ElementTree as ET
 2 
 3 tree = ET.parse("xmltest.xml")    #xmltest.xml 文件名,得到一个xml对象
 4 root = tree.getroot()
 5 print(root.tag)     #data 即得到最外层的标签名字
 6 
 7 # 遍历xml文档
 8 for child in root:
 9     print(child.tag, child.attrib)  #得到 country {'name':'Liechtenstein'}  country {'name':'Singapore'}   country {'name':'Panama'}
10     for i in child:
11         print(i.tag, i.text)
12 
13 # 只遍历year 节点
14 for node in root.iter('year'):
15     print(node.tag, node.text)
16 # ---------------------------------------
17 
18 import xml.etree.ElementTree as ET
19 
20 tree = ET.parse("xmltest.xml")
21 root = tree.getroot()
22 
23 # 修改
24 for node in root.iter('year'):
25     new_year = int(node.text) + 1  #年份加1
26     node.text = str(new_year)
27     node.set("updated", "yes")    #新加一个属性   <year> upadta='yes'>2009</year>  <year> upadta='yes'>2012</year>  <year> upadta='yes'>2012</year>
28 
29 
30 tree.write("xmltest.xml")
31 
32 # 删除node
33 for country in root.findall('country'):
34     rank = int(country.find('rank').text)
35     if rank > 50:
36         root.remove(country)
37 
38 tree.write('output.xml')

自己创建xml文档:

 1 import xml.etree.ElementTree as ET
 2  
 3  
 4 new_xml = ET.Element("namelist")
 5 name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"})
 6 age = ET.SubElement(name,"age",attrib={"checked":"no"})
 7 sex = ET.SubElement(name,"sex")
 8 sex.text = '33'
 9 name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
10 age = ET.SubElement(name2,"age")
11 age.text = '19'
12  
13 et = ET.ElementTree(new_xml) #生成文档对象
14 et.write("test.xml", encoding="utf-8",xml_declaration=True)
15  
16 ET.dump(new_xml) #打印生成的格式

 

 

subprocess模块

import subprocess
################subprocess 案例1
# a= subprocess.Popen('dir', shell=True)  #实例化一个对象,并将命令执行结果输出在屏幕;这里的subprocess自己开了一个子进程,执行结果由该子进程输出至屏幕,这个子进程和主进程无关;subprocess.Popen('dir', shell=True)和print(a)谁先输出取决于谁的执行速度快
# print(a)

# 输出
# <subprocess.Popen object at 0x000001C1386E2978>
# 2018/03/08  15:34    <DIR>          .
# 2018/03/08  15:34    <DIR>          ..
# 2018/03/08  11:24               357 client.py
# 2018/03/08  15:33             1,014 server.py
# 2018/03/08  15:34               370 subpro.py
# 2018/03/08  15:31    <DIR>          __pycache__

################subprocess 案例2
# a=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE) #实例化一个对象,命令执行结果不会在屏幕输出,该执行结果保存在subprocess的子进程中;stdout=subprocess.PIPE就是将执行结果通过管道从子进程保存到主进程上,以便通过方法可以获取到执行结果
# print(a)  #获取执行的结果

#输出:<subprocess.Popen object at 0x000001C77CCA2978>
#这时候执行结果就不会在屏幕上输出了

################subprocess 案例2 通过方法调用执行结果
# a=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE)
# #print(a.stdout.read())      #输出的是bytes类型 b' \xc7\xfd\xb6\xaf\xc6\xf7 F \xd6\xd0\xb5\xc4\xbe\xed\xca\xc7 studying\r\n \xbe\xed\xb5\xc4\xd0\xf2\xc1\xd0\xba\xc5\xca\xc7 0006-6FF7\r\n\r\n F:\\code\\day26 \xb5\xc4\xc4\xbf\xc2\xbc\r\n\r\n2018/03/08  15:56    <DIR>          .\r\n2018/03/08  15:56    <DIR>          ..\r\n2018/03/08  11:24               357 client.py\r\n2018/03/08  15:33             1,014 server.py\r\n2018/03/08  15:56             1,454 subpro.py\r\n2018/03/08  15:31    <DIR>          __pycache__\r\n               3 \xb8\xf6\xce\xc4\xbc\xfe          2,825 \xd7\xd6\xbd\xda\r\n               3 \xb8\xf6\xc4\xbf\xc2\xbc 81,451,225,088 \xbf\xc9\xd3\xc3\xd7\xd6\xbd\xda\r\n'
# print(str(a.stdout.read(),'gbk'))

# 输出:
# 2018/03/08  16:01    <DIR>          .
# 2018/03/08  16:01    <DIR>          ..
# 2018/03/08  11:24               357 client.py
# 2018/03/08  15:33             1,014 server.py
# 2018/03/08  16:01             2,132 subpro.py
# 2018/03/08  15:31    <DIR>          __pycache__
#                3 个文件          3,503 字节
#                3 个目录 81,451,225,088 可用字节
View Code

 

collections模块-python中的扩展数据类型

在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

1.namedtuple: 生成可以使用名字来访问元素内容的tuple

2.deque: 双端队列,可以快速的从另外一侧追加和推出对象

3.Counter: 计数器,主要用来计数

4.OrderedDict: 有序字典

5.defaultdict: 带有默认值的字典

namedtuple

们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:  >>> p = (1, 2)。但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。

这时,namedtuple就派上了用场:

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
print(p.x)   # 1
print(p.y)   # 2
print(p) # point(x=1,y=2)

似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:

#namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])

deque

使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。

# 队列:先进先出
import queue
q = queue.Queue
q.put(10)
q.put(5)
q.put(6)
print(q.get())  # 10
print(q.get())  #  5
print(q.get())  #  6
print(q.get())  #  不报错,程序处于阻塞状态,直到向队列中再放值时,从阻塞状态释放


#双端队列:deque
#deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x')      #向队列后面放数据
q.appendleft('y') #向队列前面放数据
print(q)   # deque(['y', 'a', 'b', 'c', 'x'])

# deque.pop()   #向队列后面取数据
# deque.popleft #向队列前面取数据
# deque.insert(1,3)  #插队

OrderedDict

使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。如果要保持Key的顺序,可以用OrderedDict

from collections import OrderedDict
d = dict([('a', 1), ('b', 2), ('c', 3)])
print(d) # dict的Key是无序的   {'a': 1, 'c': 3, 'b': 2}
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od)  # OrderedDict的Key是有序的  OrderedDict([('a', 1), ('b', 2), ('c', 3)])


od = OrderedDict()
od['z'] = 1
od['y'] = 2
od['x'] = 3  # 按照插入的Key的顺序返回
# ['z', 'y', 'x']

defaultDict

有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。

values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = {}

for value in  values:
    if value>66:
        if my_dict.has_key('k1'):
            my_dict['k1'].append(value)
        else:
            my_dict['k1'] = [value]
    else:
        if my_dict.has_key('k2'):
            my_dict['k2'].append(value)
        else:
            my_dict['k2'] = [value]
原生字典解决方法
#from collections import defaultdict
#d = defaultdict(list)  #字典的默认值为列表,括号中必须为可调用对象
#print(d['k'])   # []


from collections import defaultdict

values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = defaultdict(list)   

for value in  values:
    if value>66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)
print(my_dict)
# defaultdict(<class 'list'>, {'k1': [77, 88, 99, 90], 'k2': [11, 22, 33, 44, 55, 66]})
defaultdict字典解决方法

使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict:
即: {'k1': 大于66 , 'k2': 小于66}

>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默认值
'N/A'

Counter

Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。Counter类和其他语言的bags或multisets很相似。

c = Counter('abcdeabcdabcaba')
print c
输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

 

 

 

posted @ 2018-01-30 20:44  lriwu  阅读(418)  评论(0编辑  收藏  举报