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实例对象
son

 

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保存那些不重要的数据,不能成功地反序列化也没关系
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>
View Code

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)  # 打印生成的格式
View Code

 


应用: 计算器

  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('+',''))
View Code

 

posted @ 2018-01-10 19:52  Charonnnnn  阅读(184)  评论(0)    收藏  举报