Python基础3 - 模块
模块
模块(modue)的概念:
在计算机程序的开发过程中, 随着程序代码越写越多, 在一个文件里代码就会越来越长, 越来越不容易维护.
为了编写可维护的代码, 将很多函数分组, 分别放到不同的文件里. 这样, 每个文件包含的代码就相对较少, 很多编程语言都采用这种组织代码的方式.
在Python中,一个.py文件就称之为一个模块(Module)
优点:
1. 提高了代码的可维护性
2. 编写代码不必从零开始. 当一个模块编写完毕, 就可以被其他地方引用. 在编写程序的时候需要经常引用其他模块, 包括Python内置的模块和来自第三方的模块
模块一共三种:
python标准库
第三方模块
应用程序自定义模块 (一个.py文件就称之为一个模块)
模块导入方法:
#1. import ..语句
import cal,sys #解释器通过搜索路径找到cal.py后, 将cal = cal.py里面的所有代码 print(cal.add(1,3)) print(sys.path) #搜索路径 #print(x) #name 'x' is not defined print(cal.x) #2. from .. import ..语句 from cal import add,sub #从模块调用方法 print(add(1,9)) #print(x) #name 'x' is not defined #print(cal.x) #name 'cal.x' is not defined #3. from .. import*语句 from cal import* #与import cal不同的是, 不需要cal.add(), 可以直接调用add() print(add(3,5)) #不推荐: 因为引入的其它来源的命名, 很可能覆盖了已有的定义 # def add(x,y): # ret = x + y # return ret+2 # print(add(3,5))
from cal import add as plus #将add重新命名为plus print(plus(2,4))
包(package)
为了避免模块名冲突, Python引入了按目录来组织模块的方法, 称为包(Package)
引入了包以后, 只要顶层的包名不与别人冲突, 那所有模块都不会与别人冲突
每个包目录下都会自动生成一个__init__.py的文件
调用包就是执行包下的__init__.py文件
from web import logger #在同层的包中调用其模块 logger.logger() from web.web2 import logger #调用同层包中的包的模块 logger.logger() #logger() #'module' object is not callable from web.web2.logger import logger logger()
重要知识点:
__file__
__file__ 获取文件的相对路径
os.path.abspath(__file__) 将相对路径转换成绝对路径
os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR)
#**********IMPORTANT*********** import os import sys BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) print(BASEDIR) sys.path.append(BASEDIR) #修改根目录地址, 根目录变成原先目录的往上两层 from web import logger #在一个包中的模块调用另一个包中的模块 logger.logger()
__name__
__name__的值当调用的地方不同, 显示的结果也不同. 如果是在当前py文件, 则值为__main__, 若其他py文件,则值为其文件名
if __name__ == '__main__': 逻辑代码.... #可以让外部模块调用的时候不执行这段调试代码. 但是如果想排查问题的时, 直接执行该模块文件, 调试代码能够正常运行
time & datetime模块(4*)
时间的表达方式有3种:
1. 时间戳 2. 结构化时间 3.格式化时间
import time
#print(help(time)) print(time.time()) #1515577913.8268359 时间戳 从1970年1月1日00:00:00开始按秒计算的偏移量 #time.sleep(3) print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) #得到指定格式时间, 后面参数必须为结构化时间 print(time.strptime('2018-01-10 18:11:44','%Y-%m-%d %H:%M:%S')) #转化为结构化时间 a = time.strptime('2018-01-10 18:11:44','%Y-%m-%d %H:%M:%S') print(a.tm_year) print(a.tm_wday) #周几 #以上为重要 print(time.clock()) #计算CPU执行时间 print(time.gmtime()) #结构化时间: UTC时间: time.struct_time(tm_year=2018, tm_mon=1, tm_mday=10, tm_hour=9, tm_min=56, tm_sec=38, tm_wday=2, tm_yday=10, tm_isdst=0) print(time.localtime()) #本地时间 print(time.ctime()) #格式固定的时间 print(time.mktime(time.localtime())) #转化为时间戳 import datetime print(datetime.datetime.now()) print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分
random模块(2*)
import random
print(random.random()) # 0 - 1随机数 print(random.randint(1,8)) # 1 - 8的整数随机数,包括8 print(random.choice('charon')) print(random.choice(['123', 1, [1,2]])) print(random.sample(['123', 1, [1,2]], 2)) print(random.randrange(1,3)) #1-2 不包括3 print(chr(97)) #a #随机验证码 def v_code(): code = '' for i in range(6): add = random.choice([random.randrange(10),chr(random.randrange(65,91))]) # if i == random.randint(0,4): # add = random.randrange(10) # else: # add = chr(random.randrange(65,91)) code += str(add) print(code) v_code()
os模块(4*)
import os print(os.getcwd()) #获取当前工作目录,即当前python脚本工作的目录路径 #os.chdir(r'dirname') #改变当前脚本工作目录;相当于shell下cd r将所有内容变成字符 print(os.curdir) #返回当前目录: ('.') #os.makedirs('dirname1/dirname2') #可生成多层递归目录 #os.removedirs('dirname1/dirname2') #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 #os.mkdir('dirname') #生成单级目录;相当于shell中mkdir dirname #os.rmdir('dirname') #删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname print(os.listdir('/Users/charon/Documents/PycharmProjects/fullStake/PyBasic3')) #列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 #os.remove('xx.xx') #删除一个文件, 不能删文件夹 #os.rename("dirname","testfile") #重命名文件/目录 info = os.stat('/Users/charon/Documents/PycharmProjects/fullStake/PyBasic3/testfile') #获取文件/目录信息 print(info) print(info.st_size) #文件大小 print(os.sep) #输出操作系统特定的路径分隔符,win下为"\\",Linux和mac下为"/" print(os.linesep) #输出当前平台使用的行终止符,win下为"\t\n",Linux和mac下为"\n" print(os.pathsep) #输出用于分割文件路径的字符串 print(os.name) #输出字符串指示当前使用平台。win->'nt'; Linux和mac->'posix' print(os.system("cd") ) #运行shell命令,直接显示 print(os.environ) #获取系统环境变量 print(os.path.abspath('testfile')) #返回path规范化的绝对路径 print(os.path.split('/Users/charon/Documents/PycharmProjects/fullStake/PyBasic3/testfile')) #将path分割成目录和文件名二元组返回 print(os.path.dirname('/Users/charon/Documents/PycharmProjects/fullStake/PyBasic3/testfile')) #返回path的目录。其实就是os.path.split(path)的第一个元素 print(os.path.basename('/Users/charon/Documents/PycharmProjects/fullStake/PyBasic3/testfile')) #返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 #os.path.join(path1[, path2[, ...]]) #将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 # os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False # os.path.isabs(path) 如果path是绝对路径,返回True # os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False # os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False # os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 # os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
sys模块(3*)
与python解释器进行交互
import sys
# sys.argv 在终端命令行中, 运行python脚本后面可以跟参数,第一个元素即argv[0]是程序本身路径 # sys.exit(n) 退出程序,正常退出时exit(0) # sys.version 获取Python解释程序的版本信息 # sys.maxint 最大的Int值 # sys.path 返回模块的搜索路径(搜寻模块路径),初始化时使用PYTHONPATH环境变量的值 # sys.platform 返回操作系统平台名称 # sys.stdout.write('please:') #标准输出 # val = sys.stdin.readline()[:-1] print(sys.argv) #['/Users/charon/Documents/PycharmProjects/fullStake/PyBasic3/module_sys.py'] # if sys.argv[1] == 'xx': # xx() # elif sys.argv[2] == 'x1': # x1() print(sys.path) #sys.path.append('xx/xx/xx') #添加路径 print(sys.platform) #darwin # import os # if sys.platform == 'win32': # os.system('dir') # else: # os.system('ls')
hashlib & hmac模块(2*)
用于加密相关的操作,主要有MD5和SHA(secure hash algorithm)两种算法, SHA算法包括SHA1, SHA224, SHA256, SHA384, SHA512...
import hashlib
#MD5 m = hashlib.md5() print(m) #<md5 HASH object @ 0x101cc07b0> m.update('hello world'.encode('utf8')) #Py3.x默认通过utf8编码 而内存存的字符串类型为unicode update的参数需为二进制 print(m.hexdigest()) #16进制格式hash: 5eb63bbbe01eeed093cb22bb8f5acdc3 m.update('charon'.encode('utf8')) print(m.hexdigest()) #c0c9d30000fdb53d8dc6117f24341842 m2 = hashlib.md5() m2.update(b'hello worldcharon') print(m2.hexdigest()) #c0c9d30000fdb53d8dc6117f24341842跟上面一样 #SHA 后面数值越高, 安全性越高, 但是效率越差 s = hashlib.sha256() print(s) #<sha256 HASH object @ 0x101ce4378> s.update(b'hello world') print(s.hexdigest()) #b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 import hmac #散列消息鉴别码,简称HMAC #它内部对我们创建 key 和 内容 再进行处理然后再加密,使用HMAC时,消息通讯的双方,通过验证消息中加入的鉴别密钥K来鉴别消息的真伪 h = hmac.new('天王盖地虎'.encode('utf8'), '宝塔镇河妖'.encode('utf8')) print(h.hexdigest())
logging模块(5*)
很多程序都有记录日志的需求,日志中包含的信息可以有正常的程序访问日志,还可能有错误、警告等信息输出,
python的logging模块提供了标准的日志接口,可以通过它存储各种格式的日志.
logging的日志可以分为 debug(), info(), warning(), error() and critical() 5个级别
日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET
import logging
# logging.debug('debug message') # logging.info('info message') #以上两个级别不够,所以未打印 # logging.warning('warning message') #WARNING:root:warning message # logging.error('error message') #ERROR:root:error message # logging.critical('critical message') #CRITICAL:root:critical message logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='test.log', filemode='a') #level为debug为最低级别 所以所有消息都会打印 mode为w使每次运行都重写, 所以通常用a, 添加 logging.debug('debug') logging.info('info') logging.warning('warning') logging.error('error') logging.critical('critical') #basicConfig可用参数 # filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。 # filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 # 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用户输出的消息 #logger对象 logger = logging.getLogger() #日志对象 # 创建一个handler,用于写入日志文件 fh = logging.FileHandler('test1.log') #文件输出对象 # 再创建一个handler,用于输出到控制台 ch = logging.StreamHandler() #屏幕输出对象 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') #日志输出格式对象 #以上创建了四个对象 fh.setFormatter(formatter) ch.setFormatter(formatter) logger.addHandler(fh) #logger对象可以添加多个fh和ch对象 logger.addHandler(ch) logger.setLevel(logging.INFO) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message')
configparser模块(2*)
用于生成和修改常见配置文档
import configparser config = configparser.ConfigParser() config["DEFAULT"] = {'ServerAliveInterval': '45', 'Compression': 'yes', 'CompressionLevel': '9'} config['bitbucket.org'] = {} config['bitbucket.org']['User'] = 'hg' config['topsecret.server.com'] = {} topsecret = config['topsecret.server.com'] topsecret['Host Port'] = '50022' # mutates the parser topsecret['ForwardX11'] = 'no' # same here config['DEFAULT']['ForwardX11'] = 'yes' with open('example.ini', 'w') as configfile: config.write(configfile) 输出: [DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
读删改
import configparser config = configparser.ConfigParser() #读 config.read('example.ini') print(config.sections()) #['bitbucket.org', 'topsecret.server.com'] DEFAULT是特殊的所以不会取出 print(config.defaults()) #OrderedDict([('serveraliveinterval', '45'), ('compression', 'yes'), ('compressionlevel', '9'), ('forwardx11', 'yes')]) print('bitbucket.org' in config) #True print(config['bitbucket.org']['User']) #hg for key in config: print(key) #DEFAULT bitbucket.org topsecret.server.com for key in config['bitbucket.org']: print(key) #user compressionlevel serveraliveinterval compression forwardx11 DEFAULT中的东西一并打印 #删 config.remove_section('topsecret.server.com') config.write(open('example.ini', 'w')) #加此句覆盖原文件 print(config.has_section('topsecret.server.com')) #False #改 config.set('bitbucket.org', 'user', 'charon') config.write(open('example.ini', "w"))
re模块(5*)
正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过re模块实现
正则表达式作用: 匹配字符串
string匹配: string提供的方法是完全匹配如find(),replace(),split()...
正则匹配: 模糊匹配
字符匹配(普通字符, 元字符)
1. 普通字符: 大多数字符和字母都会和自身匹配
2. 元字符: . ^ $ * + ? { } [ ] | ( ) \
正则常用方法:
import re
#1. re.findall() #所有结果都返回到一个列表里 #2. re.search() #返回匹配到的第一个对象(object), 对象可以调用group()返回结果 ret = re.search('sb','fwgwsbfewsb') print(ret) #<_sre.SRE_Match object; span=(4, 6), match='sb'> print(ret.group()) #sb #3. re.match() #只在字符串开始匹配匹配, 返回匹配到的第一个对象(object), 对象可以调用group()返回结果 ret = re.match('asd','asdfwasde') print(ret.group()) #asd #4. re.split() ret = re.split('[a,b]','greagrbcd') #先按a来分, 之后的结果再给b来分 print(ret) #['gre', 'gr', 'cd'] ret=re.split('[ab]','abcd') #先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割 print(ret) #['', '', 'cd'] #5. re.sub() ret=re.sub('\d','abc','agfd5gre6',1) print(ret) #agfdabcgre6 ret=re.subn('\d','abc','rge5yfew6') print(ret) #('rgeabcyfewabc', 2) #6. re.compile() obj=re.compile('\d{3}') ret=obj.search('abc12345eeee') print(ret.group()) #123 #7. re.finditer() ret = re.finditer('\d', 'ds3sy4784a') print(ret) # <callable_iterator object at 0x10445f4a8> print(next(ret).group()) #3 print(next(ret).group()) #4
元字符:
import re #.: 通配符, 匹配任意字符(换行符\n之外), 但是一个.只能匹配一个 ret = re.findall('w..l','hello world') print(ret) #['worl'] #^: 匹配字符开头 ret = re.findall('^h...o', 'hdewofwhello') print(ret) #['hdewo'] #$: 匹配字符结尾 ret = re.findall('a..n$', 'adwealandqfajdn') print(ret) #['ajdn'] #*: 重复匹配, 匹配*号前的字符0次或多次, 范围[0, +oo], 等价于{0, } ret = re.findall('ba*', 'efawbaaaa') print(ret) #['baaaa'] ret = re.findall('a.*b', 'efawbaaaab') print(ret) #['awbaaaab'] #+: 重复匹配, 匹配前一个字符1次或多次, 范围[1, +oo], 等价于{1, } ret = re.findall('ab+', 'efabfewabfew') print(ret) #['ab', 'ab'] ret = re.findall('a+b', 'aaababfew') print(ret) #['aaab', 'ab'] #?: 重复匹配, 匹配前一个字符1次或0次, 范围[0, 1], 等价于{0, 1} ret = re.findall('a?b', 'aaababfew') print(ret) #['ab', 'ab'] #{}: 重复匹配 ret = re.findall('a{5}b','aaaaaaaaab') #匹配前一个字符5次 print(ret) #['aaaaab'] ret = re.findall('a{1,3}b','aaaaaaaaab') #匹配前一个字符1到3次 print(ret) #['aaab'] 贪婪匹配,以匹配到3的结果返回 #\ #反斜杠后边跟元字符去除特殊功能 #反斜杠后边跟部分普通字符实现特殊功能 # \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 匹配一个特殊字符边界,比如空格 ,&,#等 print(re.findall('\d{11}','34fdwf1143253256346321')) #['11432532563'] print(re.findall('\sasd','fds asd')) #[' asd'] print(re.findall('\wasd','fds asd')) #[] print(re.findall(r'I\b','hello, I am a PIG')) #['I'] print(re.findall('a\.','a.gj')) #['a.'] print(re.findall('a\+g','a+gj')) #['a+g'] print(re.findall("\\\\",'ad\grec')) #['\\'] print(re.findall(r'\bblow','blow')) #['blow'] 在py解释器中\b有特殊功能, 所以需加r转成字符 #(): 分组 |: 或 print(re.findall('(as)+', 'fdsasfas')) #['as', 'as'] print(re.findall('(ab)|\d','rabhdg8sd')) #['ab', ''] ret=re.search('(?P<id>\d{2})/(?P<name>\w{3})','few23/com') #(?P<xx>...)为固定格式 print(ret.group()) #23/com print(ret.group('id')) #23 print(ret.group('name')) #com print(re.findall('www.(\w+).com','www.baidu.com')) #['baidu'] findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可 print(re.findall('www.(?:\w+).com','www.baidu.com')) #['www.baidu.com'] ?:用以取消权限 #[]: 字符集, 范围匹配, 字符集可以取消元字符的特殊功能(除 \ ^ -) ret = re.findall('a[c*]n','a*n') #[c或*] print(ret) #['a*n'] ret = re.findall('[1-9a-zA-Z]','aA3n') print(ret) #['a', 'A', '3', 'n'] #^放在[]里: 取反 ret = re.findall('[^a-z]','aAt3tn') print(ret) #['A', '3']
json & pickle模块(4*)
用于序列化的两个模块
- json,用于string 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
用eval内置方法可以将一个字符串转成python对象, 不过, eval方法是有局限性的, 对于普通的数据类型,json.loads和eval都能用,但遇到特殊类型的时候,eval就不管用了,所以eval的重点还是通常用来执行一个字符串表达式,并返回表达式的值
dic = str({'1':'111'}) #需先将字典转为String才能被写入
f = open('test','w')
f.write(dic)
f = open('test','r')
data = f.read()
print(eval(data)['1'])
序列化
对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思.
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling
json
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便. (若要在不同的编程语言之间传递对象, 需用json把对象序列化为标准格式, json表示出来就是一个字符串, 可以被所有语言读取)
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:

json只支持数据类型的对象像是字典,列表,元祖等, 但不支持更高级对象像是函数,类的序列化
''' dic = {'name': 'alan', 'age':'18'} f = open('JSON_test','w') data = json.dumps(dic) f.write(data) #json.dump(dic, f) #这句等于以上两句的效果 f.close() ''' f = open('JSON_test', 'r') data = f.read() data = json.loads(data) #data = json.load(f) #这句等于以上两句的效果 print(data['name'])
f.close()
def load(): with open('xx.json') as f: info = json.load(f) return info def dump(data): with open('xx.json', 'w') as f: info = json.dump(data,f)
Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON: >>> import json >>> d = dict(name='Bob', age=20, score=88) >>> json.dumps(d) '{"age": 20, "score": 88, "name": "Bob"}' dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object。 要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化: >>> json_str = '{"age": 20, "score": 88, "name": "Bob"}' >>> json.loads(json_str) {'age': 20, 'score': 88, 'name': 'Bob'} 由于JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在Python的str与JSON的字符串之间转换。 JSON进阶 Python的dict对象可以直接序列化为JSON的{},不过,很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化: import json class Student(object): def __init__(self, name, age, score): self.name = name self.age = age self.score = score s = Student('Bob', 20, 88) print(json.dumps(s)) 运行代码,毫不留情地得到一个TypeError: Traceback (most recent call last): ... TypeError: <__main__.Student object at 0x10603cc50> is not JSON serializable 错误的原因是Student对象不是一个可序列化为JSON的对象。 如果连class的实例对象都无法序列化为JSON,这肯定不合理! 别急,我们仔细看看dumps()方法的参数列表,可以发现,除了第一个必须的obj参数外,dumps()方法还提供了一大堆的可选参数: https://docs.python.org/3/library/json.html#json.dumps 这些可选参数就是让我们来定制JSON序列化。前面的代码之所以无法把Student类实例序列化为JSON,是因为默认情况下,dumps()方法不知道如何将Student实例变为一个JSON的{}对象。 可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可: def student2dict(std): return { 'name': std.name, 'age': std.age, 'score': std.score } 这样,Student实例首先被student2dict()函数转换成dict,然后再被顺利序列化为JSON: >>> print(json.dumps(s, default=student2dict)) {"age": 20, "name": "Bob", "score": 88} 不过,下次如果遇到一个Teacher类的实例,照样无法序列化为JSON。我们可以偷个懒,把任意class的实例变为dict: print(json.dumps(s, default=lambda obj: obj.__dict__)) 因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class。 同样的道理,如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,我们传入的object_hook函数负责把dict转换为Student实例: def dict2student(d): return Student(d['name'], d['age'], d['score']) 运行结果如下: >>> json_str = '{"age": 20, "score": 88, "name": "Bob"}' >>> print(json.loads(json_str, object_hook=dict2student)) <__main__.Student object at 0x10cd3c190> 打印出的是反序列化的Student实例对象
pickle
Pickle只能用于Python, 并且可能不同版本的Python彼此都不兼容, 因此, 只能用Pickle保存那些不重要的数据, 不能成功地反序列化也没关系
import pickle def foo(): print('ok') ''' data = pickle.dumps(foo) f = open('PICKLE_test','wb') #b为bytes f.write(data) f.close() ''' f = open('PICKLE_test', 'rb') data = f.read() data = pickle.loads(data) data()
import pickle def dump(obj): f = open('xx.pk', "wb") pickle.dump(obj, f) f.close() def load(): f= open('xx.pk', "rb") infos = pickle.load(f) f.close() return infos
在程序运行的过程中,所有的变量都是在内存中,比如,定义一个dict: d = dict(name='Bob', age=20, score=88) 可以随时修改变量,比如把name改成'Bill',但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的'Bill'存储到磁盘上,下次重新运行程序,变量又被初始化为'Bob'。 我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。 序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。 Python提供了pickle模块来实现序列化。 首先,我们尝试把一个对象序列化并写入文件: >>> import pickle >>> d = dict(name='Bob', age=20, score=88) >>> pickle.dumps(d) b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.' pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object: >>> f = open('dump.txt', 'wb') >>> pickle.dump(d, f) >>> f.close() 看看写入的dump.txt文件,一堆乱七八糟的内容,这些都是Python保存的对象内部信息。 当我们要把对象从磁盘读到内存时,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象。我们打开另一个Python命令行来反序列化刚才保存的对象: >>> f = open('dump.txt', 'rb') >>> d = pickle.load(f) >>> f.close() >>> d {'age': 20, 'score': 88, 'name': 'Bob'} 变量的内容又回来了! 当然,这个变量和原来的变量是完全不相干的对象,它们只是内容相同而已。 Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系
shelve模块(3*)
shelve模块只有一个open函数, 返回类似字典的对象, 可读可写, 生成一个db格式的文件; key必须为字符串, 而值可以是python所支持的数据类型
import shelve f = shelve.open(r'SHELVE_test') f['info'] = {'name':'alan', 'age':'18'} f['info1'] = {'name':'charon', 'age':'22'} print(f.get('info')) print(f['info1']) print(f.get('info1')['name']) print(f['info1']['age']) f.close()
xml模块(2*)
xml是实现不同语言或程序之间进行数据交换的协议(在不同的编程语言之间传递对象), 跟json类似, 把对象序列化为标准格式, 但json使用起来更简单, 速度也更快
xml格式文件
<data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year updated="yes">2013</year> <gdppc>141100</gdppc> <neighbor direction="E" name="Austria" /> <neighbor direction="W" name="Switzerland" /> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year updated="yes">2013</year> <gdppc>59900</gdppc> <neighbor direction="N" name="Malaysia" /> </country> <country name="Panama"> <rank updated="yes">69</rank> <year updated="yes">2013</year> <gdppc>13600</gdppc> <neighbor direction="W" name="Costa Rica" /> <neighbor direction="E" name="Colombia" /> </country> </data>
xml模块使用
import xml.etree.ElementTree as ET
tree = ET.parse("xml_test")
root = tree.getroot()
print(root.tag) #data
# 遍历xml文档
for child in root:
print(child.tag, child.attrib) #country {'name': 'Liechtenstein'}
for i in child:
print(i.tag, i.text) #rank 2 year 2009 gdppc 141100 neighbor None neighbor None
# 只遍历year 节点
for node in root.iter('year'):
print(node.tag, node.text) #year 2009 year 2012 year 2012
# ---------------------------------------
import xml.etree.ElementTree as ET
tree = ET.parse("xml_test")
root = tree.getroot()
# 修改
for node in root.iter('year'):
new_year = int(node.text) + 1
node.text = str(new_year)
node.set("updated", "yes")
tree.write("xml_test")
# 删除node
for country in root.findall('country'):
rank = int(country.find('rank').text)
if rank > 50:
root.remove(country)
tree.write('xml_output.xml')
#创建xml文档
import xml.etree.ElementTree as ET
new_xml = ET.Element("namelist")
name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"})
age = ET.SubElement(name, "age", attrib={"checked": "no"})
sex = ET.SubElement(name, "sex")
sex.text = '33'
name2 = ET.SubElement(new_xml, "name", attrib={"enrolled": "no"})
age = ET.SubElement(name2, "age")
age.text = '19'
et = ET.ElementTree(new_xml) # 生成文档对象
et.write("xml_test1.xml", encoding="utf-8", xml_declaration=True)
ET.dump(new_xml) # 打印生成的格式
应用: 计算器
1 import re 2 3 #匹配到最里层括号 4 #print(re.search('\([^()]+\)',s).group()) 5 6 #检查表达式合法性 7 def check(s): 8 flag = True 9 if not s.count('(') == s.count(')'): 10 print('The parentheses are not balanced') 11 flag = False 12 if re.findall('[a-zA-Z]',s): 13 print('Invalid Input') 14 flag = False 15 16 return flag 17 18 #格式化字符串 19 def format(s): 20 s = s.replace(' ', '') 21 s = s.replace('++', '+') 22 s = s.replace('--', '+') 23 s = s.replace('-+', '-') 24 s = s.replace('+-', '-') 25 s = s.replace('*+', '*') 26 s = s.replace('/+', '/') 27 28 return s 29 30 def cal_mul_div(s): #计算乘除 (30+6*2+9/3) 31 regular = '[\-]?\d+\.?\d*([*/]|\*\*)[\-]?\d+\.?\d*' #[\-]?\d+\.?\d* [*/] [\-]?\d+\.?\d* 32 33 while re.findall(regular, s): 34 expression = re.search(regular, s).group() #获取表达式 6*2 35 36 if expression.count('*') == 1: 37 x,y = expression.split('*') #获取要计算的两个数 38 mul_result = float(x) * float(y) #计算结果 39 s = s.replace(expression, '+' + str(mul_result)) #将表达式替换成结果 s=(30+12+9/3) 40 s = format(s) #格式化表达式 41 42 if expression.count('/') == 1: 43 x,y = expression.split('/') 44 div_result = float(x) / float(y) 45 s = s.replace(expression, '+' + str(div_result)) #s=(30+12+3) 46 s = format(s) 47 48 if expression.count('*') == 2: 49 x,y = expression.split('**') 50 pow_result = 1 51 for i in range(int(y)): 52 pow_result *= int(x) 53 s = s.replace(expression, '+' + str(pow_result)) 54 s = format(s) 55 56 return s #s=(30+12+3) 57 58 def cal_add_sub(s): #计算加减 59 60 add_regular = '[\-]?\d+\.?\d*\+[\-]?\d+\.?\d*' #[\-]?\d+\.?\d*\ + [\-]?\d+\.?\d* 61 sub_regular = '[\-]?\d+\.?\d*\-[\-]?\d+\.?\d*' 62 63 #加法 64 while re.findall(add_regular, s): 65 add_list = re.findall(add_regular, s) 66 for add_str in add_list: 67 x,y = add_str.split('+') 68 add_result = float(x) + float(y) 69 s = s.replace(add_str, '+' + str(add_result)) 70 71 s = format(s) 72 73 #减法 74 while re.findall(sub_regular, s): 75 sub_list = re.findall(sub_regular, s) 76 for sub_str in sub_list: 77 numbers = sub_str.split('-') 78 79 #-3-5情况 80 if len(numbers) == 3: 81 sub_result = 0 82 for v in numbers: 83 if v: 84 sub_result -= float(v) 85 else: 86 x,y = numbers 87 sub_result = float(x) - float(y) 88 s = s.replace(sub_str, '+' + str(sub_result)) 89 90 s = format(s) 91 92 return s 93 94 if __name__ == '__main__': 95 source = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' 96 #source = '1 - 2 * ( (60-30 +(-9-2-5-2*3-5/3-40*4/2-3/5+6*3) * (-9-2-5-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' 97 #source = '2**3' 98 #source = '1-2*-2' 99 100 101 if check(source): 102 print('source: ', source) 103 print('eval result: ', eval(source)) 104 source = format(source) 105 print('formatting source: ', source) 106 107 while source.count('(') > 0: #while re.search('\('): 108 109 strs = re.search('\([^()]+\)', source).group() 110 rep_str = cal_mul_div(strs) #将括号里的表达式进行乘除运算 111 #print('progress:',rep_str) 112 rep_str = cal_add_sub(rep_str) #将括号里的表达式进行加减运算 113 #print('progress:',rep_str) 114 115 source = format(source.replace(strs, rep_str[1:-1])) #将括号的字符串替换为计算结果, 结果包含(), 替换时去掉(): [1:-1] 116 else: 117 #print('finalsource:', source) 118 rep_str = cal_mul_div(source) 119 #print('final:',rep_str) 120 rep_str = cal_add_sub(rep_str) 121 #print('final:',rep_str) 122 123 source = source.replace(source, rep_str) 124 125 print('my result: ', source.replace('+',''))

浙公网安备 33010602011771号