模块
(1)模块
定义:用一段代码实现某个功能的代码集合,对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。
常见的模块:os>>>>>>>系统模块
file>>>>>>>文件操作相关模块
按性质可分为三种:自定义模块、内置模块、第三方模块
(2)模块导入方法
1.import(在同个包里用import调用文件名)
import方法实质就是按照系统的环境变量sys.path中去寻找调用的文件
注意:在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉。(time,sys除外)
环境变量修改:
1 import sys,os 2 3 BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) 4 sys.path.append(BASE_DIR)
第一种调用方法:
》》》》》》》》》》》》》》》
2.from...import...
适用于调用不同包的文件
第一种调用方法,调用文件中单个函数
》》》》》》》》》》》》》》》
第二种调用方法:调用文件页全部函数
》》》》》》》》》》
运行本质:无论是第一或第二种导入模块方法,第一种首先通过sys.path找到目标wenjian,然后执行t脚本(全部执行),区别是1会将test这个变量名加载到名字空间,而2只会将add这个变量名加载进来。
3.包(package)
定义:按目录来组织模块的方法,称为包
引入了包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突。现在,view.py
模块的名字就变成了hello_django.app01.views
,类似的,manage.py
的模块名则是hello_django.manage。
每一个包目录下面都会有一个__init__.py
的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录(文件夹),而不是一个包。__init__.py
可以是空文件,也可以有Python代码,因为__init__.py
本身就是一个模块,而它的模块名就是对应包的名字。
4.if __name__ =="__main__":
作用:作为调试代码时使用,声明在它下面的代码作为调试作用,在执行页name系统默认等于“main”,在调用页name=路径,显然与__main__不相等,这样别人在调用的时候,不会将调试代码执行
##-------------cal.py def add(x,y): return x+y ##-------------main.py import cal #from module import cal def main(): cal.add(1,2) ##--------------bin.py from module import main main.main()
5.time模块
import time#调用系统内置time模块(time模块是用C语言编写的,与程序同在,并不是调用py文件来实现的)
时间戳:时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。
结构化时间:struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)
1 import time 2 #time.sleep()#推迟指定的时间运行 3 print(time.time())#打印从1970年1月日凌晨至现在的时间用秒表示 4 #1481537054.002029 5 print(time.localtime())#将一个时间戳结构化,默认以当前时间为准 6 #time.struct_time(tm_year=2016, tm_mon=12, tm_mday=12, tm_hour=18, tm_min=4, tm_sec=14, tm_wday=0, tm_yday=347, tm_isdst=0) 7 print(time.gmtime())#将一个时间戳转换为当前UTC时区,默认格林尼治时间 8 #time.struct_time(tm_year=2016, tm_mon=12, tm_mday=12, tm_hour=10, tm_min=5, tm_sec=12, tm_wday=0, tm_yday=347, tm_isdst=0) 9 print(time.mktime(time.localtime()))#将结构化时间转换为时间戳 10 #1481537602.0 11 print(time.strftime("%Y-%m-%d %X",time.localtime()))#将结构化时间以自定义格式转换为字符串时间 12 #2016-12-12 18:15:35 13 print(time.strptime("2016-09-12 12:03:24","%Y-%m-%d %X"))#将字符串时间按格式转换为结构化时间 14 #time.struct_time(tm_year=2016, tm_mon=9, tm_mday=12, tm_hour=12, tm_min=3, tm_sec=24, tm_wday=0, tm_yday=256, tm_isdst=-1) 15 print(time.clock())#cpu运行时间 16 #1.61984329635951e-06 17 print(time.ctime())#将时间戳直接转换为字符串时间 18 #Mon Dec 12 18:22:42 2016 19 print(time.asctime())#将时间戳直接转换为字符串时间 20 #Mon Dec 12 18:24:06 2016
另外,调用datetime可以直接将当前时间直接以字符串形式显示
1 import datetime 2 print(datetime.datetime.now())#以字符串形式显示当前时间 3 #2016-12-12 18:26:56.154939
6.random(随机数模块)
import random#导入random模块
1 import random#导入random模块 2 print(random.random())#随机产生一个浮点数,只能在0-1之间取 3 4 print(random.uniform(1,3))#在指定范围内随机产生一个浮点数 5 6 print(random.randint(1,3))#在范围内随机产生一个整形数,两边都可取到 7 8 print(random.choice("hello"))#在可迭代对象内随机取出一个对象 9 #h 10 print(random.randrange(1,5))#在范围内随机产生一个整形数,右边为闭区间 11 12 v = [1,2,3,4,5] 13 random.shuffle(v)#打乱原有次序 14 print(v) 15 #[2, 1, 5, 3, 4] 16 17 print(random.sample([1,2,3,4],2))#在对象中随机取出指定个数元素 18 #[4, 1]
生成验证码例子:
1 import random 2 def cod(): 3 s = "" 4 for i in range(5): 5 ret = random.choice([(random.randint(0,9)),chr(random.randint(66,122)),chr(random.randint(97,122))]) 6 s += str(ret) 7 print(s) 8 cod()
7.os模块
os模块时与系统交互的一个接口
os.getcwd()# 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") #改变当前脚本工作目录;相当于shell下cd os.curdir #返回当前目录: ('.') os.pardir #获取当前目录的父目录字符串名:('..') os.makedirs('dirname1/dirname2') #可生成多层递归目录 os.removedirs('dirname1') #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 os.mkdir('dirname') #生成单级目录;相当于shell中mkdir dirname os.rmdir('dirname') #删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname os.listdir('dirname') #列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 os.remove() #删除一个文件 os.rename("oldname","newname") #重命名文件/目录 os.stat('path/filename') #获取文件/目录信息 os.sep #输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/" os.linesep #输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" os.pathsep #输出用于分割文件路径的字符串 win下为;,Linux下为: os.name #输出字符串指示当前使用平台。win->'nt'; Linux->'posix' os.system("bash command") #运行shell命令,直接显示 os.environ #获取系统环境变量 os.path.abspath(path) #返回path规范化的绝对路径 os.path.split(path) #将path分割成目录和文件名二元组返回 os.path.dirname(path) #返回path的目录。其实就是os.path.split(path)的第一个元素 os.path.basename(path) #返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(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 os.path.join(path1[, path2[, ...]]) #将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 os.path.getatime(path) #返回path所指向的文件或者目录的最后存取时间 os.path.getmtime(path) #返回path所指向的文件或者目录的最后修改时间
8.sys模块
1 sys.argv 命令行参数List,第一个元素是程序本身路径#在调试时,可以在前面定义参数,不用再执行过程中一个个添加 2 sys.exit(n) 退出程序,正常退出时exit(0) 3 sys.version 获取Python解释程序的版本信息 4 sys.maxint 最大的Int值 5 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 6 sys.platform 返回操作系统平台名称
进度条
import sys,time for i in range(50): sys.stdout.write("%") sys.stdout.flush() time.sleep(1)
9.json/pickle
json:类似于eval用法,但eval只能处理一般的字符串类型,特殊字符串无法处理,json可以将特殊字符串及普通字符串序列化
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
1 import json 2 dic = {"a":"b"} 3 print(type(dic))#<class 'dict'> 4 v = json.dumps(dic) 5 print(type(v))#<class 'str'>#json将对象通过json特有的方法处理成了能被各种语言识别的字符串形式
1 f=open('序列化对象','w') 2 f.write(j) #-------------------等价于json.dump(dic,f) 3 f.close() 4 #-----------------------------反序列化<br> 5 import json 6 f=open('序列化对象') 7 data=json.loads(f.read())# 等价于data=json.load(f)
pickle
1 ##----------------------------序列化 2 import pickle 3 4 dic={'name':'alvin','age':23,'sex':'male'} 5 6 print(type(dic))#<class 'dict'> 7 8 j=pickle.dumps(dic) 9 print(type(j))#<class 'bytes'> 10 11 12 f=open('序列化对象_pickle','wb')#注意是w是写入str,wb是写入bytes,j是'bytes' 13 f.write(j) #-------------------等价于pickle.dump(dic,f) 14 15 f.close() 16 #-------------------------反序列化 17 import pickle 18 f=open('序列化对象_pickle','rb') 19 20 data=pickle.loads(f.read())# 等价于data=pickle.load(f) 21 22 23 print(data['age']) 24 Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
10.shelve模块
shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;key必须为字符串,而值可以是python所支持的数据类型
1 import shelve 2 3 f = shelve.open(r'shelve.txt') 4 5 # f['stu1_info']={'name':'alex','age':'18'} 6 # f['stu2_info']={'name':'alvin','age':'20'} 7 # f['school_info']={'website':'oldboyedu.com','city':'beijing'} 8 # 9 # 10 # f.close() 11 12 print(f.get('stu_info')['age'])
11.xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,由于xml出现的比json早,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
<?xml version="1.0"?> <data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year>2008</year> <gdppc>141100</gdppc> <neighbor name="Austria" direction="E"/> <neighbor name="Switzerland" direction="W"/> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year>2011</year> <gdppc>59900</gdppc> <neighbor name="Malaysia" direction="N"/> </country> <country name="Panama"> <rank updated="yes">69</rank> <year>2011</year> <gdppc>13600</gdppc> <neighbor name="Costa Rica" direction="W"/> <neighbor name="Colombia" direction="E"/> </country> </data>
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
复制代码 import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() print(root.tag) #遍历xml文档 for child in root: print(child.tag, child.attrib) for i in child: print(i.tag,i.text) #只遍历year 节点 for node in root.iter('year'): print(node.tag,node.text) #--------------------------------------- import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") 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("xmltest.xml") #删除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) ET.dump(new_xml) #打印生成的格式
12.logging模块
python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。这个模块提供不同的日志级别,默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),默认的日志格式为日志级别:Logger名称:用户输出消息。
配置日志的第一种方式:》》》》》logging.basicConfig
1 import logging 2 3 logging.basicConfig(level=logging.DEBUG,#设置日志级别 4 format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',#设置日志信息存贮格式 5 datefmt='%a, %d %b %Y %H:%M:%S',#时间格式, 6 filename='test.log',#设置日志路径 7 filemode='w')#定义写入方式 8 9 logging.debug('debug message') 10 logging.info('info message') 11 logging.warning('warning message') 12 logging.error('error message') 13 logging.critical('critical message')
具体用到的参数有:
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
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对象配置日志:
1 import logging 2 3 logger = logging.getLogger() 4 # 创建一个handler,用于写入日志文件 5 fh = logging.FileHandler('test.log') 6 7 # 再创建一个handler,用于输出到控制台 8 ch = logging.StreamHandler() 9 10 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 11 12 fh.setFormatter(formatter) 13 ch.setFormatter(formatter) 14 15 logger.addHandler(fh) #logger对象可以添加多个fh和ch对象 16 logger.addHandler(ch) 17 18 logger.debug('logger debug message') 19 logger.info('logger info message') 20 logger.warning('logger warning message') 21 logger.error('logger error message') 22 logger.critical('logger critical message')
Logger是一个树形层级结构,输出信息之前都要获得一个Logger(如果没有显示的获取则自动创建并使用root Logger)。
logger = logging.getLogger()返回一个默认的Logger也即root Logger,并应用默认的日志级别、Handler和Formatter设置。
当然也可以通过Logger.setLevel(lel)指定最低的日志级别,可用的日志级别有logging.DEBUG、logging.INFO、logging.WARNING、logging.ERROR、logging.CRITICAL。
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical()输出不同级别的日志,只有日志等级大于或等于设置的日志级别的日志才会被输出。
1 输出: 2 2016-12-18 13:36:24,518 - root - WARNING - logger warning message 3 2016-12-18 13:36:24,518 - root - ERROR - logger error message 4 2016-12-18 13:36:24,518 - root - CRITICAL - logger critical message
从这个输出可以看出logger = logging.getLogger()返回的Logger名为root。这里没有用logger.setLevel(logging.Debug)显示的为logger设置日志级别,所以使用默认的日志级别WARNIING,故结果只输出了大于等于WARNIING级别的信息。
我们再创建两个logger对象
1 ################################################## 2 logger1 = logging.getLogger('mylogger') 3 logger1.setLevel(logging.DEBUG) 4 5 logger2 = logging.getLogger('mylogger') 6 logger2.setLevel(logging.INFO) 7 8 logger1.addHandler(fh) 9 logger1.addHandler(ch) 10 11 logger2.addHandler(fh) 12 logger2.addHandler(ch) 13 14 logger1.debug('logger1 debug message') 15 logger1.info('logger1 info message') 16 logger1.warning('logger1 warning message') 17 logger1.error('logger1 error message') 18 logger1.critical('logger1 critical message') 19 20 logger2.debug('logger2 debug message') 21 logger2.info('logger2 info message') 22 logger2.warning('logger2 warning message') 23 logger2.error('logger2 error message') 24 logger2.critical('logger2 critical message')
打印:
这里有两个个问题:
<1>我们明明通过logger1.setLevel(logging.DEBUG)将logger1的日志级别设置为了DEBUG,为何显示的时候没有显示出DEBUG级别的日志信息,而是从INFO级别的日志开始显示呢?
原来logger1和logger2对应的是同一个Logger实例,只要logging.getLogger(name)中名称参数name相同则返回的Logger实例就是同一个,且仅有一个,也即name与Logger实例一一对应。在logger2实例中通过logger2.setLevel(logging.INFO)设置mylogger的日志级别为logging.INFO,所以最后logger1的输出遵从了后来设置的日志级别。
<2>为什么logger1、logger2对应的每个输出分别显示两次?
这是因为我们通过logger = logging.getLogger()显示的创建了root Logger,而logger1 = logging.getLogger('mylogger')创建了root Logger的孩子(root.)mylogger,logger2同样。而孩子,孙子,重孙……既会将消息分发给他的handler进行处理也会传递给所有的祖先Logger处理。
12.hashlib模块》》》》加密操作
主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
1 import hashlib 2 3 m=hashlib.md5()# m=hashlib.sha256() 4 5 m.update('hello'.encode('utf8')) 6 print(m.hexdigest()) #5d41402abc4b2a76b9719d911017c592 7 8 m.update('alvin'.encode('utf8')) 9 10 print(m.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af 11 12 m2=hashlib.md5() 13 m2.update('helloalvin'.encode('utf8')) 14 print(m2.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af
以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。
1 import hashlib 2 3 # ######## 256 ######## 4 5 hash = hashlib.sha256('898oaFs09f'.encode('utf8')) 6 hash.update('alvin'.encode('utf8')) 7 print (hash.hexdigest())#e79e68f070cdedcfe63eaf1a2e92c83b4cfb1b5c6bc452d214c1b7e77cdfd1c7
python 还有一个 hmac 模块,它内部对我们创建 key 和 内容 再进行处理然后再加密:
1 import hmac 2 h = hmac.new('alvin'.encode('utf8')) 3 h.update('hello'.encode('utf8')) 4 print (h.hexdigest())#320df9832eab4c038b6c1d7ed73a5940