模块 |
模块这个概念其实在很多地方都可以听到,它表示的是对功能的封装、代码的复用。
模块能做什么呢?在代码中,有很多设计好的框架、模式,我们无需花费大量时间去摸索。在Python中有许多内置模块和第三方开发人员提供的第三方模块,可以直接使用。
一、引用模块
每一个.py文件其实就是一个模块,下面一起来动手编写一个模块。创建一个module.py文件,在文件中编写以下代码:
#文件名module.py def add(x,y): return x+y def sub(x,y): return x-y
代码中定义了add()和sub()函数,模块定义后,在其他文件中如需导入,可使用import 文件名(模块名)。如果要导入多个模块,可以逗号分隔。
创建一个文件,命名为bin.py文件,在bin.py中通过import引入module模块,并调用module模块下的add()方法,代码如下:
#bin.py #第一种导入方式 import module print(module.add(5,6))
运行代码,输出结果为11。第二种导入的方式是使用from 模块名 import 方法名或类名:
#bin.py #第二种导入方式 from module import add print(add(5,6))
运行一下,发现上面的两种导入方式结果都一样,都可以正常运行。两种导入方式的区别如下:
- 调用方式不同。
- import module是将被导入的模块的名称放入当前的模块内;而from module import add是将被导入的函数或者变量的名称放入到当前操作的模块内。
- 如果想导入一个模块的所有项目,使用from 模块名 import *,如果只想导入某些内容,可以在import后面依次列出。
使用from 模块名 import 方法名或类名导入时有一个坑点,即导入的模块内容和文件内的函数、变量名称不能冲突,否则会被覆蓋,可以通过as来起别名,起一个其他的名字就完美地避免了重名的问题。
#bin.py from module import add as newadd def add(): print('不是导入模块的add') add() print(newadd(5,6))
在bin.py文件中自定义了一个add()函数,但引入的module模块下也有一个名为add()的函数。为了解决命名冲突给module模块下的add()函数起别名为newadd(),这时在下面调用时就可以区分开了。
二、内置模块
内置模块是指无须安装就可以直接使用的模块。Python中把一些常用的操作封装到内置模块内,在编写过程中如果需要用到无须编写直接引用即可,可以更好地加快开发效率。
1.time模块
time模块通常是由三种方式来表示:
(1)时间戳,返回的是浮点型类型,它表示时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
(2)结构化时间,例如:time.struct_time(tm_year=2019, tm_mon=7, tm_mday=22, tm_hour=14, tm_min=53, tm_sec=2, tm_wday=0, tm_yday=203, tm_isdst=0)。
(3)格式化字符串,例如:2019-7-22。
我们来使用代码来实现这些方式以及各种的方式转换。
下面来看一些time模块中经常用到的方法,代码如下:
#时间戳 #计算 print(time.time()) #结构化时间--当地时间 print(time.localtime()) t = time.localtime() print(t.tm_year) #结构化时间--UTC print(time.gmtime()) #将结构化时间转换成时间戳 print(time.mktime(time.localtime())) print(time.mktime(time.gmtime())) #将时间戳转换成结构化时间 print(time.localtime(time.time())) print(time.gmtime(time.time())) #将结构化时间转换成字符串时间 print(time.strftime('%Y-%m-%d %X',time.localtime())) print(time.strftime('%Y-%m-%d %X',time.gmtime())) #将字符串时间转换成结构化时间 print(time.strptime('2019:7:18:9:43:36','%Y:%m:%d:%X')) print(time.asctime()) #直接将结构化时间转换成字符串时间,以固定格式 print(time.ctime()) #直接将时间戳转换成字符串时间,以固定格式 print(time.sleep()) #延时
既然讲到time模块,我们就提提datetime模块,datetime模块用于操作日期/时间,包括时间的格式化输出以及日期的计算和获取。
datetime模块常用方法代码如下:
import datetime print(datetime.datetime.now()) #当前时间 print(datetime.date.today()) #格式化输出 print(datetime.datetime.now()+datetime.timedelta(days=10)) #比现在加10天 print(datetime.datetime.now()+datetime.timedelta(days=-10)) #比现在减10天 print(datetime.datetime.now()+datetime.timedelta(seconds=120)) #比现在+120s
2.random模块
random是一个用于生成随机数的模块。
下面来看一些random模块中经常用到的方法,代码如下:
import random print (random.random()) #在(0,1)随机取 print(random.randint(1,3)) #在[1,3]随机取 print(random.randrange(1,3)) #在[1,3)随机取 print(random.choice([11,22,33])) #在[11,22,33]中随机取一个 print(random.sample([11,22,33,44,55,66,77],2)) #在[11,22,33,44,55,66,77]汇总随机取两个 print(random.uniform(1,3)) #在(1,3)随机取,float item = [1,3,5,7,9] random.shuffle(item) #打乱次序 print(item)
3.sys模块
sys模块提供了一系列有关Python运行环境的变量和函数。
下面来看一些sys模块中经常用到的方法,代码如下:
import sys sys.argv #命令行参数list,第一个元素是程序本身路径 sys.path #返回模块的搜索路径 sys.exit() #退出程序,正常退出时exit(0) sys.version #获取Python解释程序的版本信息 sys.maxint #最大的Int值 sys.platform #返回操作系统平台名称
4.os模块
os模块包含普通的操作系统功能,如文件操作、目录等,与具体的平台无关。
下面来看一些os模块中经常用到的方法,代码如下:
import os print(os.getcwd()) #获取当前工作目录 os.chdir('test1') #修改当前工作目录 '..':返回上一层 print(os.getcwd()) os.makedirs('dicname1/dicname2') #可生成多层递归目录 os.removedirs('dicname1') #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,以此类推 os.mkdir() #生成单级目录 os.rmdir() #删除单级空目录 os.listdir() #列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 os.remove() #删除一个文件 os.rename() #重命名文件/目录 print(os.stat()) #获取文件/目录信息 os.sep #输出操作系统特定的路径分隔符 os.linesep #输出当前平台使用的行终止符 os.pathsep #输出用于分割文件路径的字符串 os.name #输出字符串指示当前使用平台 os.system() #运行shell命令,直接显示 os.environ #获取系统环境变量 os.path.split(path) #将path分割成目录和文件名二元组返回 os.path.dirname(path) #返回path的目录 os.path.abspath(path) #返回path规范化的绝对路径 os.path.basename(path) #返回path最后的文件名 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 a = 'C:\Users\Administrator\PycharmProjects' b = 'untitled1\day22\sss.py' os.path.join(a,b) #路径拼接 os.path.getatime(path) #返回path所指向的文件或者目录的最后存取时间 os.path.getmtime(path) #返回path所指向的文件或者目录的最后修改时间
5.json模块
json模块用于字符串 和 python数据类型间进行转换。
json模块提供了四种功能:dumps、dump、loads、load。代码如下:
import json dic = {'name':'alex'} dic_str = json.dumps(dic) #将 Python 对象编码成 json 数据 f = open('new_hello','w') f.write(dic_str)
f.close() #相当于 dic = {'name':'alex'} f = open('new_hello','w') json.dump(dic,f) #将 json 数据通过特殊的形式转换为只有 Python 认识的字符串并写入文件 f.close()
f_read = open('new_hello','r') data = json.loads(f_read.read()) #将已编码的 json 数据解码为 Python 对象 f_read.close()
print(data) print(type(data)) #相当于 data = json.load(f) #将一个包含 json 格式数据的可读文件解码为一个 Python 对象并写入文件 f.close()
print(data) print(type(data))
6.pickle模块
pickle模块用于字节 和 python数据类型间进行转换。
pickle模块与json模块功能十分类似。具体代码实现如下:
import pickle dic = {'name':'alex','age':'18'} print(type(dic)) #<class 'dict'> j = pickle.dumps(dic) print(type(j)) #<class 'bytes'> f = open('序列化对象_pickle','wb') #注意是w是写入str,wb是写入bytes f.write(j) f.close() #-------------------- f = open('序列化对象_pickle','rb') data = pickle.loads(f.read()) print(data['age'])
7.shelve模块
shelve模块是对象持久化保存方法,将对象保存到文件里面,默认的数据存储文件是二进制的。
import shelve f = shelve.open(r'shelve.txt') #目的:将一个字典放入文本f={} f['stu1_info'] = {'name':'alex','age':'18'} f['stu2_info'] = {'name':'lisi','age':'20'} f['school_info'] = {'website':'oldboydu.com','city':'beijing'} f.close() #----------------- print(f.get('stu1_info')['age'])
8.xml模块
xml模块实现不同语言或者程序之间进行数据交换的协议。
首先我们先定义一个xml文件,文件命名为xml_lesson,具体代码如下:
<data> <country TEL="13511122233" name="CTO"> <zongjian age="19" name="zhangsan"> <jingli age="22" name="wangwu"> .....(其他属性) </jingli> </zongjian> <zongjian age="19" name="lisi">2023</zongjian> </country> <country name="CEO"> <rank updated="yes">5</rank> <year updated="yes">2027</year> <gdppc>59900</gdppc> <neighbor direction="N" name="Malaysia" /> </country> <country name="COO"> <rank updated="yes">69</rank> <year updated="yes">2027</year> <gdppc>13600</gdppc> <neighbor direction="W" name="Costa Rica" /> <neighbor direction="E" name="Colombia" /> </country> </data>
利用Python中xml模块对xml_lesson文件进行操作,具体代码如下:
import xml.etree.ElementTree as ET tree = ET.parse('xml_lesson') #解析 root = tree.getroot() print(root.tag) # 打印根节点标签 #遍历xml文档 for child in root: print(child.tag,child.attrib,child.text) #tag标签,attrib属性,test内容 for i in child: print(i.tag,i.attrib,i.text) #只遍历year 节点 for node in root.iter('year'): print(node.tag,node.text) #修改 for node in root.iter('year'): new_year = int(node.text)+1 node.text = str(new_year) node.set('updated','yes') #修改或者添加属性 tree.write('xml_lesson') #删除node for country in root.findall('country'): rank = int(country.find('rank').text) if rank > 50: root.remove(country) tree.write('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('test.xml',encoding='utf-8',xml_declaration=True)
9.logging
logging模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。
logging模块的日志级别(CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志级别设置为WARNING。
(1)通过logging.basicConfig函数对日志的输出格式及方式做相关配置。
import logging logging.basicConfig( level = logging.DEBUG, #调整级别 filename = 'logger.long', #日志写进文件 filemode = 'w', #文件打开方式 format = '%(asctime)s %(filename)s [%(lineno)d] %(message)s' #日志打印的格式 ) logging.debug('debug message') logging.info('info message') logging.warning('marning message') logging.error('error message') logging.critical('critical message')
运行之后,在logger.long文件的内容如下:
2019-07-22 17:07:18,884 logging_module.py [12] debug message 2019-07-22 17:07:18,884 logging_module.py [13] info message 2019-07-22 17:07:18,884 logging_module.py [14] marning message 2019-07-22 17:07:18,884 logging_module.py [15] error message 2019-07-22 17:07:18,884 logging_module.py [16] critical message
可以看出logging.basicConfig函数中参数有很多,例如:
logging.basicConfig函数各参数: filename: 指定日志文件名 filemode: 和file函数意义相同,指定日志文件的打开模式,'w'或'a' format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所示: %(levelno)s: 打印日志级别的数值 %(levelname)s: 打印日志级别名称 %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0] %(filename)s: 打印当前执行程序名 %(funcName)s: 打印日志的当前函数 %(lineno)d: 打印日志的当前行号 %(asctime)s: 打印日志的时间 %(thread)d: 打印线程ID %(threadName)s: 打印线程名称 %(process)d: 打印进程ID %(message)s: 打印日志信息 datefmt: 指定时间格式,同time.strftime() level: 设置日志级别,默认为logging.WARNING stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略
(2)通过logger对日志的输出、屏幕的输出格式及方式做相关配置。
def logger(): logger = logging.getLogger() fh = logging.FileHandler('test_log') #向文件发送内容 ch = logging.StreamHandler() #向屏幕发送内容 fm = logging.Formatter('%(asctime)s %(message)s') #设置格式 fh.setFormatter(fm) ch.setFormatter(fm) logger.addHandler(fh) logger.addHandler(ch) logger.setLevel('DEBUG') #设置级别 return logger #---------------------可以在其他文件调用上面日志格式 logger = logger() logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critcal')
我们来分析下面这段代码,看有什么问题:
import logging logger1 = logging.getLogger('mylogger') logger1.setLevel('DEBUG') logger2 = logging.getLogger('mylogger') logger2.setLevel('INFO') fh = logging.FileHandler('test_log new') ch = logging.StreamHandler() logger1.addHandler(fh) logger1.addHandler(ch) logger2.addHandler(fh) logger2.addHandler(ch) logger1.debug('logger1 debug message') logger1.info('logger1 info message') logger1.warning('logger1 warning message') logger1.error('logger1 error message') logger1.critical('logger1 critical message') logger2.debug('logger2 debug message') logger2.info('logger2 info message') logger2.warning('logger2 warning message') logger2.error('logger2 error message') logger2.critical('logger2 critical message')
执行结果为:
logger1 info message
logger1 warning message
logger1 error message
logger1 critical message
logger2 info message
logger2 warning message
logger2 error message
logger2 critical message
可以看出的问题就是我们明明通过logger1.setLevel(logging.DEBUG)将logger1的日志级别设置为了DEBUG,为何显示的时候没有显示出DEBUG级别的日志信息,而是从INFO级别的日志开始显示呢?
这就是”mylogger”这个子对象是唯一的,都会按最后约定的级别输出。如果需要logger1和logger2按自己约定好的级别进行输出,则需要对logger2中的子对象”mylogger”修改成”mylogger.sontree”即可。
10.configparser模块
configparser模块应用于配置文件。比如创建一个配置文件:
import configparser #创建配置文件 config = configparser.ConfigParser() #config-{},相当于一个空字典 config['DEFAULT'] = { 'Serveraliveinterval' : '45', 'compressionlevel' : 9, 'compression' : 'yes' } config['bitbucket.org']={ 'User' : 'hg' } config['topsecret.server.com']={ 'Host Port' : '50022', 'ForwardX11' : 'no' } with open('example.ini','w')as f: config.write(f)
执行结果为:
[DEFAULT] serveraliveinterval = 45 compressionlevel = 9 compression = yes [bitbucket.org] user = hg [topsecret.server.com] host port = 50022 forwardx11 = no
下面代码是配置文件的增、删、改、查操作:
import configparser #查看配置文件 config = configparser.ConfigParser() config.read('example.ini') print(config.sections()) #默认的不打印,只打印除[DEFAULT]以外的块名 print('bybebong.com' in config) #判断块是否存在 print(config['bitbucket.org']['User']) #遍历时会把默认的一起遍历 for key in config['bitbucket.org']: print(key) print(config.options('bitbucket.org')) #相当于遍历的值放在列表中 print(config.items('bitbucket.org')) #相当于遍历的值以元组的形式放在列表中 print(config.get('bitbucket.org','compression')) #查看key的值 #增、删、改,写进一个新文件 config = configparser.ConfigParser() config.read('example.ini') config.add_section('yuan') #新加一个块 config.set('yuan','k1','11111') #在yuan块中添加一个键值对 config.remove_section('topsecret.server.com') #删除块 config.remove_option('bitbucket.org','user') #删除一个键值对 config.write(open('i.cfg','w'))
11.hashlib模块
hashlib模块提供了很多加密的算法。如md5常用来用户密码加密处理、sha1常用来做数字签名等。
md5加密是hashlib里面加密方法的一种,也是我们比较常用的,先来看一个对一段数据进行加密:
import hashlib obj = hashlib.md5() obj.update('hello'.encode('utf8')) #对hello进行加密运算 print(obj.hexdigest())
执行结果为:
5d41402abc4b2a76b9719d911017c592
这就是加密后的结果,md5加密是不可逆的,网上一些破解md5的网站都是通过暴力破解的方式,即通过自己加密内容和用户输入要破解的密文进行匹配。
既然有可能被破解,那么我们应该怎么完善一下md5呢?比如可以进行md5加盐。
所谓加盐,就是加一些辅助的调料,这里称为salt值。下面我们用的代码来实现加盐的md5加密:
import hashlib obj = hashlib.md5('ch'.encode('utf8')) #ch相当于加的调料 obj.update('hello'.encode('utf8')) print(obj.hexdigest())
执行结果为:
bd40147470ae0a3169b9c9137573b1e0
加盐之后,两次加密之后的结果显然不同。在加盐过程中,首先需要编码指定好一个盐值,然后将md5后的结果拼接盐值再次进行md5加密。