python 模块

Python模块是实现特定功能的代码集合,有助于提高代码的可维护性。模块分为自定义、内置和开源(第三方)模块。自定义模块通过`import`或`from...import`导入,内置模块如time、random、os等,开源模块需安装。导入模块时,Python解释器根据sys.path查找。导入机制包括:`import module`、`from module import name`和`from module import *`。了解模块分类和导入机制对于有效利用Python资源至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、模块(modue):

1)模块的定义:用一堆代码实现了某个功能的代码集合

类似与函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合,而对于一个复杂的功能来说,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个.py文件组成的代码集合就称为模块,如:os是系统相关的模块,file是文件操作相关的模块

2)使用模块有什么好处

最大的好处是大大提高了代码的可维护性。

其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。

3)模块分类

      1)自定义模块

      2)内置模块

      3)开源模块(第三方模块)

另外,使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。
二、介绍模块分类

1、自定义模块

     1)情景

      情景一

     情景二

     

    2)import导入方法

    import:    1)执行对应的文件     2)引入变量名
    (1) import 语句

   import module1[, module2[,... moduleN]

       当我们使用import语句的时候,Python解释器是怎样找到对应的文件的呢?答案就是解释器有自己的搜索路径,存在sys.path里。  

1

2

['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu',

'/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']  

因此若像我一样在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉。

    ( 2) from…import 语句

from  modname import name1[, name2[, ... nameN]]

这个声明不会把整个modulename模块导入到当前的命名空间中,只会将它里面的name1或name2单个引入到执行这个声明的模块的全局符号表。

     (3)  From…import* 语句

1

from modname import *

这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。

(4) 运行本质 

1

2

#1 import test

#2 from test import add  

无论1还是2,首先通过sys.path找到test.py,然后执行test脚本(全部执行),区别是1会将test这个变量名加载到名字空间,而2只会将add这个变量名加载进来。

过程:

导入模块其实就是在告诉Python解释器去解释那个py文件

如果导入的是一个py文件,解释器就解释该py文件

如果导入的是一个包,解释器就解释该包下的__init__.py文件

Python中,导入模块时是根据那个路径作为基准来进行的呢?

默认搜寻模块的地址:可通过sys模块中的sys.path来查看 

  >>> import sys

  >>> sys.path

  ['', '/usr/local/lib/python35.zip', '/usr/local/lib/python3.5',    '/usr/local/lib/python3.5/plat-linux', '/usr/local/lib/python3.5/lib-dynload',         '/usr/local/lib/python3.5/site-packages']

  如果sys.path路径列表没有你想要的路径,可以通过 sys.path.append('路径') 添加。

通过os模块可以获取各种目录

import sys
import os

pre_path = os.path.abspath('../')
sys.path.append(pre_path)

 

2、开源模块(第三方模块)

    1)下载安装 -- 有两种方式

         方法一           方法二
yum ...下载源码
pip ...解压源码
apt-get ...进入目录
 编译源码    python setup.py build
 安装源码    python setup.py install

 

1 下载源码
2 解压源码
3 进入目录
4 编译源码  python setup.py build
5 安装源码  python setup.py install

注:在使用源码安装时,需要使用到gcc编译和Python开发环境,所以,要事先将环境安装上

1 yum install gcc
2 yum install python-devel
3 或
4 apt-get python-dev

安装成功后,模块会自动安装到sys.path中的某个目录中,如:

1 /usr/lib/python3.5/site-packages/

导入模块的方式同自定义模块导入的方式一样

 

3、内置模块

在安装完成python后,python本身就带有库,这个库叫做python内置库,那么内置模块也被称之为标准库

1)time模块

在python中,通常有以下几种方式来表示时间:

时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量,

格式化的时间字符串:

元组(struct_time):struct_time元组共有9个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)

import time

print(time.time())  # 返回当前时间的时间戳 1537413568.4175603
print(time.localtime())  # 将一个时间戳转换为当前时区
print(time.gmtime())  # localtime()方法类似,gmtime()方法是将一个时间戳转换为UTC时区(0时区)的struct_time。

#结果
1537413652.9021907

time.struct_time(tm_year=2018, tm_mon=9, tm_mday=20, tm_hour=11, tm_min=20, tm_sec=52, tm_wday=3, tm_yday=263, tm_isdst=0)

time.struct_time(tm_year=2018, tm_mon=9, tm_mday=20, tm_hour=3, tm_min=20, tm_sec=52, tm_wday=3, tm_yday=263, tm_isdst=0)



print(time.ctime())  # Thu Sep 20 11:21:55 2018
print(time.gmtime(time.time()-9000))#返回utc时间的struc时间对象格式
#日期转换成时间戳
time_struct = time.strptime("2016-12-12 18:24:30","%Y-%m-%d %X")
print(time_struct)
#将日期字符串转换struct时间对象格式

#将struct时间对象转换成时间戳
time_struct = time.strptime("2016-12-12 18:24:30","%Y-%m-%d %X")
time_one_struct = time.mktime(time_struct)
print(time_one_struct) #1481538270.0
#将时间戳转换为字符串格式
print(time.gmtime(time.time()-99999)) # 将utc时间戳转换成struct_time格式
#将utc struct_time格式转换为指定的字符串格式
print(time.strftime("%Y-%m-%d %X",time.gmtime()))
#sleep()  #线程推迟自定的时间运行,单位为秒
#clock()
#这个需要注意啦,在不同的系统上含义不同,在unix系统上,它返回的是'进程时间',用秒表示的浮点数(时间戳)
#在windows中,第一次调用,返回的是进程运行的实际时间,而第二次之后的调用是自第一次调用后到现在的运行
#的时间,即两次的时间差

#结果
Thu Sep 20 11:21:55 2018
time.struct_time(tm_year=2018, tm_mon=9, tm_mday=20, tm_hour=0, tm_min=51, tm_sec=55, tm_wday=3, tm_yday=263, tm_isdst=0)
time.struct_time(tm_year=2016, tm_mon=12, tm_mday=12, tm_hour=18, tm_min=24, tm_sec=30, tm_wday=0, tm_yday=347, tm_isdst=-1)
1481538270.0
time.struct_time(tm_year=2018, tm_mon=9, tm_mday=18, tm_hour=23, tm_min=35, tm_sec=16, tm_wday=1, tm_yday=261, tm_isdst=0)
2018-09-20 03:21:55

 

 

 

import datetime,time

#显示当前时间日期
print(datetime.datetime.now())   #  2018-09-20 11:26:52.167597

#当前时间+3天
print(datetime.datetime.now() + datetime.timedelta(3))   #2018-09-23 11:26:52.167597

#当前时间-3天
print(datetime.datetime.now() + datetime.timedelta(-3))  #2018-09-17 11:26:52.167597

#当前时间+30分
print(datetime.datetime.now() + datetime.timedelta(hours=3))  #2018-09-20 14:26:52.167597

#时间替换
c_time = datetime.datetime.now()
print(c_time.replace(minute=3,hour=2))  #2018-09-20 02:03:52.167597  将小时数与分钟数替换

#时间戳直接转换为日期格式
print(datetime.date.fromtimestamp(time.time()))  # 2018-09-20

 

2)random模块

import random

#0,1之间时间生成的浮点数  float
print(random.random())

#随机生成传入参数范围内的数字 即 1,2,3
print(random.randint(1,3))

#随机生成传入参数范围内的数字,range顾头不顾尾
print(random.randrange(1,3))

#随机选择任意一个数字
print(random.choice([1,'23',[4,5]]))

#随机选择任意两个数字
print(random.sample([1,'23',[4,5]],2))

3)os模块

os模块提供对操作系统进行调用的接口

import os

#获取当前的工作目录,即当前Python脚本工作的目录路径,相当于Linux中的pwd
print(os.getcwd())

# 改变当前脚本工作目录,相当于shell下的cd
print(os.chdir("当前目录名称/../../"))  

# 可生成多层递归目录
os.makedirs("dirname1/dirname2")  

# 若目录为空,则删除,并递归到上一级目录,如也为空则删除,以此类推
os.removedirs("dirname1/dirname2")  


# 获取当前的工作目录,即当前Python脚本工作的目录路径,相当于Linux中的pwd
print(os.getcwd())

# 改变当前脚本工作目录,相当于shell下的cd
print(os.chdir("D:\\PycharmProjects\\fullstack"))  

# 可生成多层递归目录
os.makedirs("dirname1/dirname2")  

# 若目录为空,则删除,并递归到上一级目录,如也为空则删除,以此类推
os.removedirs("dirname1//dirname2")  

# 生成当级目录,相当于shell中的mkdir dirname
os.mkdir("dirname")  

# 删除单级空目录,若目录不为空则无法删除,报错,相当于shell中的rm dirname
os.rmdir("namedir") 
 
# 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表的方式打印
print(os.listdir("dirname")) 

# 删除一个文件
os.remove("filename")  

# 重命名文件/目录
os.rename("oldname","newname")  

 # 获取文件/目录信息
os.stat("path/filename") 

#  输出操作系统特定的路径分隔符,win下为'\\',Linux下为'/'
print(os.sep)  

 # 输出当前平台使用的行终止符,win下为'\t\n',Linux下为'\n'
print(os.linesep) 

print(os.pathsep)  # 输入用户分割文件路径的字符串
print(os.name)  # 输出字符串指示当前使用平台,win->'nt',Linux->'posix'
print(os.system("bash command"))  # 运行shell命令,直接显示
print(os.environ)  # 获取系统环境变量
print(os.path.abspath(path))  # 返回path规范化的绝对路径
print(os.path.split(path))  # 将path分割成目录和文件名,二元组返回
print(os.path.dirname(path)) # 返回path的目录,其实就是os.path.split(path)的第一个元素
print(os.path.basename(path))  # 返回path最后的文件名,如果path以/或\结尾,就会返回空值
print(os.path.exists(path)) # 如果path存在,返回True,如果不存在,就返回False
print(os.path.isabs(path))  # 如果path是绝对路径返回True,不存在返回False
print(os.path.join(path1[,path2[,path3]])) # 将多个路劲组合后返回,

# 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
print(os.path.join(path1,path2,**)) 

#返回path所指向的文件或者目录的最后存取时间,可通过time模块,转换时间格式
a = os.path.getatime(r"D:\PycharmProjects\fullstack\homework_oneday\module\module_file")
print(time.gmtime(a))

print(os.path.getmtime(path))  #返回path指向的文件或者目录的最后修改时间

4)sys模块

import sys

sys.argv  # 命令行参数List,第一个元素是程序本身路径
sys.version  # 获取Python解释程序的版本信息
sys.exit(n)  # 退出程序,正常退出时exit(0)
sys.path  # 返回模块的搜索路劲,初始话时使用PYTHONPATH环境变量的值
sys.platform  # 返回操作系统平台名称
sys.stdout.write('please:')  # 标志输出
val = sys.stdin.readline()[:-1]

5)skelve模块

skelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写,key必须为字符串,而值可以是python所支持的所有的数据类型

import shelve


f = shelve.open('shelve_file.txt')
# f["stu1_info"] = {"name":"jack","age":"18"}
# f["stu2_info"] = {"name":"rain","age":"21"}
# f["stu3_info"] = {"name":"tom","age":"34"}
# f.close()
print(f.get("stu1_info"))  # {'name': 'jack', 'age': '18'}
print(f.get("stu1_info")["age"])  # 18

6)xml模块

xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但是json使用起来更简单,因为json还没有诞生,大家只能选择用xml,至今很多传统公司和金融行业的很多系统的接口还主要是xml

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("xml_file.xml")
root = tree.getroot()  #获取xml文件对象
print(root.tag)  # data

# 遍历xml文档
for item in root:
    print(item.tag,item.attrib)
    for i in item:
        print(i.tag,i.attrib)

# 只遍历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_file.xml")

# 删除
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)  # 打印生成格式

7)configparser模块

用于对特定的配置进行操作,当前模块的名称在Python3.x版本中变更为configparser

我们来看一下Linux中常见软件,配置格式如下:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
  
[bitbucket.org]
User = hg
  
[topsecret.server.com]
Port = 50022
ForwardX11 = no

如果想用Python生成一个这样的文档怎么做呢 

import configparser

config = configparser.ConfigParser()
config["DEFAULT"] = {"ServerAliveInterval":"45",
                        "Compression":"yes",
                        "CompressionLevel":"9"}
config["bitbucket.org"] = {}
config["bitbucket.org"]["User"] = "jack"
config["topsecret.server.com"] = {}
topsecret = config["topsecret.server.com"]
topsecret["Host Port"] = "5002"
topsecret["Forwardx11"] = "no"
config["DEFAULT"]["Forwardx11"] = "yes"
with open("example.ini" ,"w",encoding="utf-8") as configfile:
    config.write(configfile)

增删改查

import configparser

config = configparser.ConfigParser()
# 查
print(config.sections())  # []
config.read("example.ini")
print(config.sections())  # ['bitbucket.org', 'topsecret.server.com']
print("bytebong.com" in config)  # False
print(config["bitbucket.org"]["User"])  # jack
print(config["topsecret.server.com"]["Forwardx11"])  # no
for key in config["bitbucket.org"]:
    print(key)

#user
#compressionlevel
#serveraliveinterval
#compression
#forwardx11

print(config.options("bitbucket.org"))
# ['user', 'compressionlevel', 'serveraliveinterval', 'compression', 'forwardx11']
print(config.items("bitbucket.org"))
# [('compressionlevel', '9'), ('serveraliveinterval', '45'), ('compression', 'yes'), ('forwardx11', 'yes'), ('user', 'jack')]
print(config.get("bitbucket.org","compression"))  # yes

#---------------------------------------------删,改,增
(config.write(open('i.cfg', "w")))


config.add_section('yuan')

config.remove_section('topsecret.server.com')
config.remove_option('bitbucket.org','user')

config.set('bitbucket.org','k1','11111')

config.write(open('i.cfg', "w"))

 8)hashlib

用于加密相关的操作,代替了md5模块和sha模块,只要提供SHA1,SHA224,SHA256,SHA384,SHA512,MD5算法

import md5
hash = md5.new()
hash.update('admin')
print hash.hexdigest()
import sha

hash = sha.new()
hash.update('admin')
print hash.hexdigest()
import hashlib

m = hashlib.md5()  # m = hashlib.sha256
m.update("admin".encode("utf-8"))
print(m.hexdigest())  # 21232f297a57a5a743894a0e4a801fc3

m.update("jack".encode("utf-8"))
print(m.hexdigest())  # f75c16c62166e263e5f8d84881c15de9

m2 = hashlib.md5()
m2.update("jack".encode("utf-8"))
print(m2.hexdigest())  # 4ff9fc6e4e5d5f590c4f2134a8cc96d1


m3 = hashlib.md5()
m3.update("hellejack".encode("utf-8"))
print(m3.hexdigest())  # 7a477fa1d4918d6af53fc6115e5b4376

以上的加密算法虽然非常厉害,但还是存在缺陷,即:通过撞库可以反解,所以,有必要对加密算法中添加自定义key再来做加密 

import hashlib

m = hashlib.md5("dfs9Ys".encode("utf-8"))
m.update("jack".encode("utf-8"))
print(m.hexdigest())  # 3566e566a677ba0fb8b7ec637ff707a9

python中还有一个hmac模块,它对我们创建key和内容再进行处理然后再加密

import hmac

h = hmac.new("hello".encode("utf-8"))
h.update("jack".encode("utf-8"))  # 8570e3f986ef9099bfcd2e46181d0be7
print(h.hexdigest())

 9)logging

用于便捷记录日志且线程安全的模块

1、简单应用

import logging

logging.debug("debug message")
logging.info("info message")
logging.warning("warning message")
logging.error("error message")
logging.critical("critical message")

# 输出

WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message

#在默认情况下Python的logging模块将日志打印到了标准输出中,请只显示大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING
日志级别等级:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET
默认的日志格式

WARNING:root:warning message
日志级别:logger名称:用户输出消息

2、 灵活配置日志级别,日志格式,输出位置

import logging  

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='/tmp/test.log',  
                    filemode='w')  
  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')

查看输出

Sat, 12 Nov 2016 05:58:47 logg.py[line:9] DEBUG debug message
Sat, 12 Nov 2016 05:58:47 logg.py[line:10] INFO info message
Sat, 12 Nov 2016 05:58:47 logg.py[line:11] WARNING warning message
Sat, 12 Nov 2016 05:58:47 logg.py[line:12] ERROR error message
Sat, 12 Nov 2016 05:58:47 logg.py[line:13] CRITICAL critical message

在logging.bashicConfit()函数可通过具体参数来更改logging模块默认行为.

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:用户输出的消息

3、logger对象

上述几个例子中,我们了解到了logging.debug(),logging.info(),logging.warning(),logging.error(),

logging.critical()分别用以记录不同级别的日志信息,logging.basicConfig()用默认日志格式(Formatter)为日志

系统建立一个默认的流处理器(StreamHandler),设置基于配置(如日志级别等),并加到root logger(根logger中)这几个logging模块级别的函数,另外还有一个模块级别的函数是logging.getLogger([name])(返回一个logger对象,如果没有指定名字将返回root logger)

在这里我们先来看一个最简单的过程:

import logging

logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.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.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')

先简单介绍一下,logging库提供了多个组件:

Logger:对象提供应用程序可直接使用的接口

Handler:发送日志到适当的目的地

Filter:提供了过滤日志信息发方法

Formatter:指定日志显示格式

1.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()分别输出不同级别的日志,只有日志等级大于或等于设置的日志级别的日志才会被输出

import logging

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') 

只输出了

2016-12-15 16:32:57,329 - root - WARNING - logger warning message
2016-12-15 16:32:57,329 - root - ERROR - logger error message
2016-12-15 16:32:57,330 - root - CRITICAL - logger critical message

从这个输出可以看出logger = logging.getLogger()返回的Logger名为root。这里没有用logger.setLevel(logging.Debug)显示的为logger设置日志级别,所以使用默认的日志级别WARNIING,故结果只输出了大于等于WARNIING级别的信息。

2.如果我们在创建两个logger对象

import logging

logger1 = logging.getLogger('mylogger')
logger1.setLevel(logging.DEBUG)

logger2 = logging.getLogger('mylogger')
logger2.setLevel(logging.INFO)

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')

 

输出结果

2016-12-15 17:22:34,837 - mylogger - INFO - logger1 info message
2016-12-15 17:22:34,837 - mylogger - INFO - logger1 info message
2016-12-15 17:22:34,837 - mylogger - WARNING - logger1 warning message
2016-12-15 17:22:34,837 - mylogger - WARNING - logger1 warning message
2016-12-15 17:22:34,837 - mylogger - ERROR - logger1 error message
2016-12-15 17:22:34,837 - mylogger - ERROR - logger1 error message
2016-12-15 17:22:34,837 - mylogger - CRITICAL - logger1 critical message
2016-12-15 17:22:34,837 - mylogger - CRITICAL - logger1 critical message
2016-12-15 17:22:34,838 - mylogger - INFO - logger2 info message
2016-12-15 17:22:34,838 - mylogger - INFO - logger2 info message
2016-12-15 17:22:34,838 - mylogger - WARNING - logger2 warning message
2016-12-15 17:22:34,838 - mylogger - WARNING - logger2 warning message
2016-12-15 17:22:34,838 - mylogger - ERROR - logger2 error message
2016-12-15 17:22:34,838 - mylogger - ERROR - logger2 error message
2016-12-15 17:22:34,838 - mylogger - CRITICAL - logger2 critical message
2016-12-15 17:22:34,838 - mylogger - 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处理。

        ok,那么现在我们把

# logger.addHandler(fh)

# logger.addHandler(ch)  注释掉,我们再来看效果:

2016-12-15 17:24:09,250 - mylogger - INFO - logger1 info message
2016-12-15 17:24:09,251 - mylogger - WARNING - logger1 warning message
2016-12-15 17:24:09,251 - mylogger - ERROR - logger1 error message
2016-12-15 17:24:09,251 - mylogger - CRITICAL - logger1 critical message
2016-12-15 17:24:09,251 - mylogger - INFO - logger2 info message
2016-12-15 17:24:09,251 - mylogger - WARNING - logger2 warning message
2016-12-15 17:24:09,251 - mylogger - ERROR - logger2 error message
2016-12-15 17:24:09,251 - mylogger - CRITICAL - logger2 critical message

因为我们注释了logger对象显示的位置,所以才用了默认方式,即标准输出方式。因为它的父级没有设置文件显示方式,所以在这里只打印了一次。

孩子,孙子,重孙……可逐层继承来自祖先的日志级别、Handler、Filter设置,也可以通过Logger.setLevel(lel)、Logger.addHandler(hdlr)、Logger.removeHandler(hdlr)、Logger.addFilter(filt)、Logger.removeFilter(filt)。设置自己特别的日志级别、Handler、Filter。若不设置则使用继承来的值。

<3>Filter
     限制只有满足过滤规则的日志才会输出。
     比如我们定义了filter = logging.Filter('a.b.c'),并将这个Filter添加到了一个Handler上,则使用该Handler的Logger中只有名字带          a.b.c前缀的Logger才能输出其日志。

 

 filter = logging.Filter('mylogger') 

     logger.addFilter(filter)

     这是只对logger这个对象进行筛选

     如果想对所有的对象进行筛选,则:

      filter = logging.Filter('mylogger') 

      fh.addFilter(filter)

      ch.addFilter(filter)

      这样,所有添加fh或者ch的logger对象都会进行筛选。

完整代码1:

import logging

logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log')

# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 定义一个filter
filter = logging.Filter('mylogger')
fh.addFilter(filter)
ch.addFilter(filter)

# logger.addFilter(filter)
logger.addHandler(fh)
logger.addHandler(ch)




logger.setLevel(logging.DEBUG)

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')

##################################################
logger1 = logging.getLogger('mylogger')
logger1.setLevel(logging.DEBUG)

logger2 = logging.getLogger('mylogger')
logger2.setLevel(logging.INFO)

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')

完整代码2

#coding:utf-8  
import logging  
  
# 创建一个logger    
logger = logging.getLogger()  
  
logger1 = logging.getLogger('mylogger')  
logger1.setLevel(logging.DEBUG)  
  
logger2 = logging.getLogger('mylogger')  
logger2.setLevel(logging.INFO)  
  
logger3 = logging.getLogger('mylogger.child1')  
logger3.setLevel(logging.WARNING)  
  
logger4 = logging.getLogger('mylogger.child1.child2')  
logger4.setLevel(logging.DEBUG)  
  
logger5 = logging.getLogger('mylogger.child1.child2.child3')  
logger5.setLevel(logging.DEBUG)  
  
# 创建一个handler,用于写入日志文件    
fh = logging.FileHandler('/tmp/test.log')  
  
# 再创建一个handler,用于输出到控制台    
ch = logging.StreamHandler()  
  
# 定义handler的输出格式formatter    
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')  
fh.setFormatter(formatter)  
ch.setFormatter(formatter)  
  
#定义一个filter  
#filter = logging.Filter('mylogger.child1.child2')  
#fh.addFilter(filter)    
  
# 给logger添加handler    
#logger.addFilter(filter)  
logger.addHandler(fh)  
logger.addHandler(ch)  
  
#logger1.addFilter(filter)  
logger1.addHandler(fh)  
logger1.addHandler(ch)  
  
logger2.addHandler(fh)  
logger2.addHandler(ch)  
  
#logger3.addFilter(filter)  
logger3.addHandler(fh)  
logger3.addHandler(ch)  
  
#logger4.addFilter(filter)  
logger4.addHandler(fh)  
logger4.addHandler(ch)  
  
logger5.addHandler(fh)  
logger5.addHandler(ch)  
  
# 记录一条日志    
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')  
  
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')  
  
logger3.debug('logger3 debug message')  
logger3.info('logger3 info message')  
logger3.warning('logger3 warning message')  
logger3.error('logger3 error message')  
logger3.critical('logger3 critical message')  
  
logger4.debug('logger4 debug message')  
logger4.info('logger4 info message')  
logger4.warning('logger4 warning message')  
logger4.error('logger4 error message')  
logger4.critical('logger4 critical message')  
  
logger5.debug('logger5 debug message')  
logger5.info('logger5 info message')  
logger5.warning('logger5 warning message')  
logger5.error('logger5 error message')  
logger5.critical('logger5 critical message')

三、包(package)

如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。

假设,如下图,我的两个time_file.py模块名字重名了,但是这两个模块的功能都不相同,如果这两个模块都在同一级目录中,那么我在其他地方要调用这个time_file.py模块,那么这个时候就会发生冲突,在这里我们就可以通过包来组织模块,避免冲突,

方法是:选择一个顶层包名,引入包以后,只要顶层的包名不与别人冲突,那这个包里面的模块都不会与别人冲突了。

请注意:每个包目录下来都会有一个__init__.py的文件,这个文件必须是存在的,否则,Python就不把这个目录当成普通目录,而不是一个包,__init__.py可以是空文件,也可以有python代码,__init__.py本身就是一个文件,它的模块命就是对应的包名,它一般由于做接口文件

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值