
模块
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Python中,一个.py文件就称之为一个模块(Module)。
使用模块有什么好处?
最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。
使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。点这里查看Python的所有内置函数。
你也许还想到,如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。
举个例子,一个abc.py的文件就是一个名字叫abc的模块,一个xyz.py的文件就是一个名字叫xyz的模块。
现在,假设我们的abc和xyz这两个模块名字与其他模块冲突了,于是我们可以通过包来组织模块,避免冲突。方法是选择一个顶层包名,比如test,按照如下目录存放:
引入了包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突。现在,abc.py模块的名字就变成了test.abc,类似的,xyz.py的模块名变成了test.xyz。
请注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是test。
模块分为三种:
自定义模块
第三方模块
内置模块
自定义模块
1、定义模块
a.同一目录下定义多个模块
b.同一目录不同子目录下定义模块
c.自定义多个模块
2、导入模块
1
2
3
4
|
import module #调用模块,一般调用内置模块或第三方模块
from module import xxx #导入某个模块的某个方法
from module import xxx as rename #导入模块并定义模块别名
from module import * #导入模块的所有方法
|
导入模块其实就是告诉python解释器去解释那个py文件。
导入模块时,如果想直接通过import module的方式导入,根据sys.path路径作为基准来进行,也就是说你所导入的模块必须在sys.path路径下,如果没有,则import module方式不能成功导入。
1
2
3
4
5
6
7
8
9
10
|
import sys
print (sys.path)
结果: [ 'J:\\python_program\\S13\\day06\\模块\\test' , 'J:\\python_program\\S13' ,
'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python35\\python35.zip' ,
'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python35\\DLLs' ,
'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python35\\lib' ,
'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python35' ,
'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python35\\lib\\site-packages' ]
|
如果sys.path中没有需要的路径,可以通过sys.path.append('路径')添加。
将当前程序所在路径加入到sys.path中:
1
2
3
4
5
|
import sys
import os
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(project_path) print (sys.path)
|
在Python中,安装第三方模块,是通过包管理工具pip完成的。
如果你正在使用Mac或Linux,安装pip本身这个步骤就可以跳过了。
如果你正在使用Windows,确保安装python时勾选了pip和Add python.exe to Path。
在命令提示符窗口下尝试运行pip,如果Windows提示未找到命令,可以重新运行安装程序添加pip。
安装模块命令:
1
2
3
4
5
6
7
8
9
|
pip install requests import requests 加载requests、json模块
import json
reponse.encoding = 'utf-8'
dic = json.loads(reponse.text)
print (dic, type (dic))
|
内置模块
1、sys模块--用于提供python解释器相关操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
sys.argv #命令行参数List,第一个元素是程序本身路径
sys.exit(n) #退出程序,正常退成时exit(0)
sys.maxsize #最大的Int值
sys.path #返回模块的搜索路径,初始化时使用PYTHONPATH环境变量
sys.platfrom #操作系统平台名称
sys.stdin #输入相关
sys.stdout #输出相关
sys.stderror #错误相关的
import sys
sys.stdout.write( 'Hello World!' ) #屏幕输出hello world
import sys
print ( 'Please enter your name:' )
name = sys.stdin.readline()[: - 1 ] #获取命令行输入
print ( 'Hi,%s' % name)
|
进度百分比:
1
2
3
4
5
6
7
8
9
10
11
|
import sys,time
def view_bar(num,total):
rate = float (num) / float (total)
rate_num = int (rate * 100 )
r = '\r%d%%' % (rate_num)
sys.stdout.write(r) #屏幕输出百分比
sys.stdout.flush() #屏幕强制刷新
if __name__ = = '__main__' :
for i in range ( 0 , 100 ):
time.sleep( 0.1 )
view_bar(i, 100 )
|
2、os模块---用于提供系统级别的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir( "dirname" ) 改变当前脚本工作目录;相当于shell下cd
os.curdir 返回当前目录: ( '.' )
os.pardir 获取当前目录的父目录字符串名:( '..' )
os.makedirs( 'dir1/dir2' ) 可生成多层递归目录
os.removedirs( 'dirname1' ) 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir( 'dirname' ) 生成单级目录;相当于shell中mkdir dirname
os.rmdir( 'dirname' ) 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir( 'dirname' ) 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove() 删除一个文件 os.rename( "oldname" , "new" ) 重命名文件 / 目录
os.stat( 'path/filename' ) 获取文件 / 目录信息
os.sep 操作系统特定的路径分隔符,win下为 "\\",Linux下为" / "
os.linesep 当前平台使用的行终止符,win下为 "\t\n" ,Linux下为 "\n"
os.pathsep 用于分割文件路径的字符串 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所指向的文件或者目录的最后修改时间 |
3、json、pickle-----python中用于序列化的两个模块
json:用于字符串和python基本数据类型间进行转换
pickle:用于python特有类型和python基本数据类型之间进行转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import json
dic = { 'k1' : 999 }
result = json.dumps(dic) #将字典转化为字符串
print (result, type (result))
#执行结果:{"k1": 999} <class 'str'> str = '{"k1":"biubiubiu"}' #注意:反序列化时,内部必须用双引号
result = json.loads( str ) #将字符串转化为字典
print (result, type (result))
#执行结果:{'k1': 'biubiubiu'} <class 'dict'> import json
li = [ 11 , 22 , 33 , 44 ]
json.dump(li, open ( 'db' , 'w' )) #将列表持久化到文件中
li1 = json.load( open ( 'db' , 'r' )) #将列表从文件中读取出来
print (li1, type (li1))
#执行结果:[11, 22, 33, 44] <class 'list'> import pickle
li = [ 11 , 22 , 33 , 44 ]
a = pickle.dumps(li) #dumps/loads都是在内存中进行的
print (a)
#执行结果: b'\x80\x03]q\x00(K\x0bK\x16K!K,e.' |
通过以上例子,不难看出pickle转换都是将python的基本数据类型转换成2进制格式,那么我们存文件时,也需要以二进制的方式存
1
2
3
4
5
6
|
li = [ 11 , 22 , 33 , 44 ]
a = pickle.dump(li, open ( 'db' , 'wb' ))
result = pickle.load( open ( 'db' , 'rb' ))
print (result)
#执行结果:[11, 22, 33, 44] |
4、time模块
时间相关的操作,时间有三种表示方式:
时间戳 1970年1月1日之后的秒,即:time.time()
格式化的字符串 2014-11-11 11:11, 即:time.strftime('%Y-%m-%d')
结构化时间 元组包含了:年、日、星期等... time.struct_time 即:time.localtime()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
import time
print (time.time()) #返回从1970年到现在的时间戳 1465447994.0811183
print (time.ctime()) #输出当前系统时间 Thu Jun 9 12:53:48 2016
print (time.ctime(time.time() - 86400 )) #将时间戳转换为字符串格式 Wed Jun 8 12:56:53 2016
print (time.gmtime(time.time() - 86400 )) #将时间戳UTC转换成struct_time格式
print (time.localtime(time.time() - 86400 )) #将本地时间的时间戳转换成struct_time格式:
print (time.mktime(time.localtime())) #将struct_time格式转换回时间戳格式 1465448495.0
print (time.strftime( % Y_ % m_ % d % H: % M)) #将struct_time格式转换成指定的字符串格式 2016-06-09 05:03:22
print (time.strptime( "2016-06-09" , "%Y-%m-%d" )) #将字符串格式转换成struct_time格式
import datetime
print (datetime.date.today) #输出格式2016-06-09
print (datetime.date.fromtimestamp(time.time() - 86400 )) #输出昨天的时间
current_time = datetime.datetime.now()
print (current_time) #输出2016-06-09 13:09:28.271793
print (current_time.timetuple()) #返回struct_time格式
print (current_time.repace( 2015 , 5 , 20 )) #输出2015-05-20 13:11:34.505876,返回当前时间,但指定的值将被替换
str_to_date = datetime.datetime.strptime( "21/11/06 16:30" , "%d/%m/%y %H:%M" ) #将字符串转换成日期格式 2006-11-21 16:30:00
new_date = datetime.datetime.now() + datetime.timedelta(day = 10 ) #比现在加十天
new_date = datetime.datetime.now() + datetime.timedelta(hours = - 10 ) #比现在减10小时
print (new_date)
2016 - 06 - 19 13 : 18 : 14.567043 #10天以后
2016 - 06 - 09 03 : 18 : 36.311825 #10小时之前
下面是时间格式化的占位符的表示方法: % Y Year with century as a decimal number.
% m Month as a decimal number [ 01 , 12 ].
% d Day of the month as a decimal number [ 01 , 31 ].
% H Hour ( 24 - hour clock) as a decimal number [ 00 , 23 ].
% M Minute as a decimal number [ 00 , 59 ].
% S Second as a decimal number [ 00 , 61 ].
% z Time zone offset from UTC.
% a Locale's abbreviated weekday name.
% A Locale's full weekday name.
% b Locale's abbreviated month name.
% B Locale's full month name.
% c Locale's appropriate date and time representation.
% I Hour ( 12 - hour clock) as a decimal number [ 01 , 12 ].
% p Locale's equivalent of either AM or PM.
|
5、logging日志模块
logging的日志可以分为debug(),info(),warning(),error(),critical()5个级别
这几个级别的严重性从高到低critical>error>warning>info>debug
模块的使用:
1
2
3
4
5
6
7
8
|
import logging
logging.warning( "user[mysql]attempted wrong pasword more than 3 times" )
logging.critical( "server had down" )
输出结果: WARNING:root:user[mysql]attempted wrong pasword more than 3 times
CRITICAL:root:server had down |
将日志信息写入文件:
1
2
3
4
|
logging.basicConfig(filename = 'error.log' ,level = logging.INFO)
logging.debug( 'This is debug message...' )
logging.info( "it's info message biubiubiu..." )
logging.warning( "warning biubiubiu dididi ....." )
|
文件中会有info、warning的输出信息,没有debug的信息。因为定义时指定
level=logging.INFO,故不会只会写入info及info级别以上的信息。
以上程序可以写入到文件中,但是没有打印日志的时间,下面就加上打印时间:
1
2
3
4
5
|
logging.basicConfig( format = '%(asctime)s %(message)s' , datefmt = '%m/%d/%Y %I:%M:%S %p' )
logging.warning( 'what is time now' )
执行结果: 06 / 16 / 2016 04 : 29 : 53 PM what is time now
|
如果想同时把log打印在屏幕和日志里,就需要在掌握一些复杂的知识:
logger:提供了应用程序可以直接使用的接口;
handler:将(logger创建的)日志记录发送到合适的目的输出;
filter:提供细度设备来决定输出那条日志记录;
formatter:决定日志记录的最终输出格式。
下面就分别介绍一下上面这四种方法在实际工作中的应用:
1、Logger
Logger输出信息之前必须获得一个Logger(如果没有显示的获取则自动创建并使用root Logger)。
这里所说的Logger就是程序的执行用户,默认是root执行,可以指定zabbix、nginx等用户。
logger = logging.getLogger() 返回一个默认的Logger也即root Logger,并应用默认的日志级别WARNING、Handler和Formatter设置
logging.setLevel(level) 指定最低的日志级别,可用日志级别有:logging.DEBUG、logging.INFO、logging.WARNING、logging.ERROR、 logging.CRITICAL.
注意:此处定义为全局的日志级别, 全局日志级别是一个基调,下面局部日志级别只能比全局日志级别高或者相等才能正常输出,不能比全局日志级别低。
1
2
3
4
|
import logging
logger = logging.getLogger( 'mylogger' ) #定义程序的执行用户
logger.setLevel(logging.DEBUG) #定义全局日志级别
|
2、Handler
Handler对象负责发送相关的信息到指定目的地,有几个常用的Handler方法:
Handler.setLevel(level): 指定日志级别,低于全局日志级别的日志将被忽略
Handler.setFormatter():给这个handler选择一个Formatter
Handler.addFilter(filt)、Handler.remove(filt):新增或删除一个filter对象
可以通过addHandler()方法为Logger添加多个Handler:
1
2
|
logger.addHandler( '输出到屏幕' )
logger.addHandler( '输出到文件' )
|
下面列举几种定义Handler输出的方法:
1
2
3
4
5
6
7
8
9
10
11
|
logging.StreamHandler #向终端输出日志信息
logging.FileHandler #向文件是输出日志信息
logging.handlers.RotatingFileHandler #类似上面的FileHandler,但是它可以管理文件大小;当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件输出
logging.handlers.TimedRotatindFileHandler #和RotatindFileHandler类似,不过它没有通过判断文件大小来决定何时重新创建文件,而是间隔一定时间就自动创建新的日志文件
logging.handlers.SocketHandler #使用TCP协议,将日志信息发送到网络
logging.handlers.DatagramHandler #使用UDP协议,将日志信息发送到网络
logging.handlers.SysLogHandler #日志输出到syslog
logging.handlers.SMTPHandler #远程输出日志到邮件地址
logging.handlers.MemoryHandler #日志输出到内存中的指定buffer
logging.handlers.HTTPHandler #通过'GET'或'POST'远程输出到HTTP服务器
|
3、Formatter
Formatter对象设置日志信息最后的规则、结构和内容,默认时间格式为%Y-%m-%d %H:%M:%S
1
|
formatter = logging.Formatter( '%(asctime)s - %(name)s' - % (levelname)s - % (message)s')
|
Formatter参数中用到的字符串的占位符和logging.basicConfig()函数用到的相同。
4、Filter
限制只有满足过滤规则的日志才会输出,比如我们定义了filter=logging.Filter('a,b,c'),并将这个Filter添加给了一个Handler上,则使用该Handler的Logger中只有名字带有a,b,c前缀的Logger才会输出其日志。
上面分别介绍了一下logging模块的高级用法中的Logger、Handler、Formatter和Filter,下面通过一个小例子把几个用法串到一起使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
import logging
#create logger logger = logging.getLogger( 'haifeng' ) #指定程序执行的用户名
logger.setLevel(logging.DEBUG) #全局日志级别,也是最低日志级别
#create console handler and set level to debug ch = logging.StreamHandler() #指定屏幕输出
ch.setLevel(logging.DEBUG) #指定屏幕输出日志级别
#create file handler and set level to warning fh = logging.FileHandler( 'access.log' ) #指定输出到文件里
fh.setLevel(logging.WARNING) #日志级别为WARING
#create formatter formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(lineno)s - %(message)s' ) #定义日志内容的格式
#add formatter to ch and fh ch.setFormater(formatter) #定义屏幕输出按照上面定义的日志格式
fh.setFormatter(formatter) #文件输出也是这个格式
#add ch and fh to logger logger.addHandler(ch) #添加Handler打印到终端上的方式到logger
logger.addHandler(fh) #添加Handler打印到文件中的方式到logger
#'application' code logger.debug( 'debug message' )
logger.info( 'info message' )
logger.warn( 'warn message' )
logger.error( 'error message' )
logger.critical( 'critical message' )
|