Python学习笔记五(模块与包)

一、模块

1.模块介绍

一个模块就是包含了一组功能的python文件,可以通过import导入模块使用。

python中模块分为四个类别:

  a) 使用python编写的.py文件

  b) 已被编译为共享库或DLL的C或C++扩展

  c) 把一系列模块组织到一起的文件夹,文件夹内有__init__.py文件,称该文件夹为包

  d) 使用C编写并链接到python解释器的内置模块

定义my_module.py模块,模块名为my_module

print("from my_module.py")
num = 2222
def read1():
    print("my_module模块:",num)
def read2():
    print("my_module模块")
    read1()
def change():
    global num
    num = 0

2.模块导入

模块可以包含可执行的语句和函数定义,这些语句目的是初始化模块,在模块名第一次遇到import语句时才执行。模块第一次导入后就将模块名加载到内存了,后续import次模块是对已加载到内存的模块对象增加了一次引用,不会重新执行模块内语句。

第一次模块导入时会做的三件事:

  a) 为模块源文件创建新的名词空间,在模块中定义的函数和方法若是使用global时访问的就是这个名词空间。

  b) 在新创建的命名空间中执行模块中包含的代码。

  c) 使用模块名创建名字来引用命名空间

每个模块都是一个独立的名称空间,定义在模块中的函数,把这个模块名称空间当做全局名称空间。不用担心自定义模块中全局变量被导入时与使用者全局变量冲突。

import  my_module
num = 111
print(my_module.num)  #输出的是模块全局名称空间中num的值
import  my_module
def read1():
    print("from test...")
my_module.read1()  #执行的是模块全局名称空间中的read1
import  my_module
num = 111
my_module.change()  #实际修改的模块自身全局名称空间中的num
print(num)          #输出的是调用函数全局名称空间中的num

可以使用import my_module as mm 为模块起别名,对编写可扩展代码有用。比如两个模块xmlreader.py和csvreader.py,都定义了函数read_data(filename)用来从文件中读取一些数据,但采用不同的输入格式。可以编写代码来选择性地挑选读取模块。

if file_format == "xml":
    import xmlreader as reader
elif file_format == "csv":
    import csvreader as reader
data = reader.read_date(filename)

from...import和import的区别:使用from...import...是将模块中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用相关名字即可,无需使用模块前缀。缺点是容易和当前执行文件中的名字冲突。

函数中调用的变量或其他函数,仍然在函数定义文件中的全局名称空间中找。

from my_module import read1
num = 1111
read1()  #调用read1时,仍然从my_module全局名称空间中找num
from my_module import read2
def read1():
    print("aaaa")
read2()   #仍然从my_module全局名称空间中找read1
from my_module import read1
def read1():
    print("aaaa")
read1()   #重名会覆盖,以本文件中定义为准,覆盖掉导入的

from...import *会吧模块中所有的不是以下划线开头的名字都导入到当前位置,一般不建议使用该方式。可以使用__all__控制*,比如在模块中新增一行__all__=['read1','read2'] 使用from...import *就只能导入这两个名字。

3.py文件区分两种用途:模块和脚本

一个python文件的两种用途:

  a) 当做脚本执行:__name__=="__main__"

  b) 当做模块被导入使用:__name__=="模块名"

  可以控制.py文件在不同应用场景下执行不同的逻辑

4.模块搜索路径

模块查找顺序为:内存中已加载的模块-》内置模块-》sys.path路径中包含的模块

  a) 第一次导入模块时,会检查该模块是否已经被加载到内存(当前执行文件名称空间对应内存)中,有则直接引用。

    python解释器启动时会自动加载一些模块到内存,使用sys.module可以查看

  b) 如果内存没有,解释器会查找同名的内建模块

  c) 如果内建模块也没有,则从sys.path给出的目录列表中依次查找模块文件。

初始化后,python程序可以修改sys.path,路径在前面的优先于标准库被加载。通过列表添加的方式添加新路径。

import sys
sys.path.append("D:\\")
sys.path.insert(0,"E:\\")   #最前面优先被搜索到
print(sys.path)

二、包

1.包的介绍

 包是一个包含有__init__.py的文件夹,用于组织python模块名称空间的方式。主要目的为提高程序的结构性和可维护性。

包的导入语句也分为import和from...import...两种,但是无论哪种,在导入时都必须遵循一个原则:凡是导入时带点的,点的左边必须是一个包,否则非法。导入成功后,无此限制。

import包,产生的名称空间的名字来源于原文件,即包下的__init__.py文件,导入包本质就是导入改文件。

2.包的使用

单独导入包名时不会导入包中所有子模块,需要在包下的__init__.py中添加from . import 模块,并且from后import导入的模块,不能带点,否则会有语法错误。f

from aa.bb import * 导入的是bb包中__init__.py文件中的名字,并非bb文件夹下的模块名字。

绝对导入是以包的根路径作为起始位置,相对导入是使用.或者..的方式作为起始。

包以及包中包含的模块都是用于被导入的,不是被直接执行的。环境变量以执行文件为准。比如两个模块均在同一目录下,被其他路径的文件导入,那么这两个模块之间若要导入时,不可直接import,需要添加操作执行文件的路径来导入。

三、日志模块

1.日志级别

日志分为五个级别:CRITICAL、ERROR、WARNING、INFO、DEBUG,依次可以使用数字50、40、30、20、10代替,NOSET表示不设置级别,用0代替。当日志设置为某一级别后,只会记录比该级别更高级别的日志。

#默认界别为warning,输出就只输出warning、error、critical级别日志
import logging
logging.debug("debug_test")
logging.info("info_test")
logging.warning("warning_test")
logging.error("error_test")
logging.critical("critical_test")

2.logging模块的Formatter,Handler,Logger,Filter对象

产生日志对象logger,过滤日志对象Filter,接收日志并控制输出位置的对象Handler,日志格式对象Formatter。

 1 import logging
 2 #1.Logger,产生日志
 3 logger = logging.getLogger("访问日志")
 4 #2.Filter,几乎用不上
 5 #3.Handler,接收logger传来的日志,格式化,并打印到终端或文件
 6 sh = logging.StreamHandler()
 7 fh1 = logging.FileHandler("f1.log",encoding="utf-8")
 8 fh2 = logging.FileHandler("f2.log",encoding="utf-8")
 9 #4.Formatter,日志格式化
10 formatter1 = logging.Formatter(
11     fmt = '%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
12     datefmt='%Y-%m-%d %H-%M-%S %p',
13 )
14 formatter2 = logging.Formatter(
15     fmt = '%(asctime)s : %(message)s',
16     datefmt='%Y-%m-%d %H-%M-%S %p',
17 )
18 formatter3 = logging.Formatter(
19     fmt='%(asctime)s : %(module)s : %(message)s',
20     datefmt='%Y-%m-%d %H-%M-%S %p',
21 )
22 #5.为handler绑定日志格式
23 sh.setFormatter(formatter1)
24 fh1.setFormatter(formatter2)
25 fh2.setFormatter(formatter3)
26 #6.为logger绑定handler
27 logger.addHandler(sh)
28 logger.addHandler(fh1)
29 logger.addHandler(fh2)
30 #7.设置日志级别:logger对象的日志级别应该小于或等于handler的日志级别,否则低级别日志无法到到handler
31 logger.setLevel(10)
32 sh.setLevel(10)
33 fh1.setLevel(10)
34 fh2.setLevel(10)
35 #8.测试
36 logger.debug("debug test")
37 logger.info('info test')
38 logger.warning('warning test')
39 logger.error('error test')
40 logger.critical('critical test')
logging详细用法

在实际编程中使用以上方法较麻烦,可以将日志相关对象创建写在一个py文件中作为模块来调用。

 1 import os
 2 import logging.config
 3 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #获取上一级目录绝对路径
 4 LOG_PATH=os.path.join(BASE_DIR,'log','access.log')
 5 COLLECT_PATH=os.path.join(BASE_DIR,'log','collect.log')
 6 DB_PATH=os.path.join(BASE_DIR,'db','user')
 7 
 8 # 定义三种日志输出格式 开始
 9 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
10                   '[%(levelname)s][%(message)s]'
11 
12 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
13 
14 id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
15 
16 # log配置字典
17 LOGGING_DIC = {
18     'version': 1,
19     'disable_existing_loggers': False,
20     'formatters': {
21         'standard': {
22             'format': standard_format
23         },
24         'simple': {
25             'format': simple_format
26         },
27         'id_simple' : {
28             'format' : id_simple_format
29         },
30     },
31     'filters': {},
32     'handlers': {
33         #打印到终端的日志
34         'console': {
35             'level': 'DEBUG',
36             'class': 'logging.StreamHandler',  # 打印到屏幕
37             'formatter': 'simple'
38         },
39         #打印到文件的日志,收集info及以上的日志
40         'default': {
41             'level': 'DEBUG',
42             'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
43             'formatter': 'standard',
44             'filename': LOG_PATH,  # 日志文件
45             'maxBytes': 1024*1024*5,  # 日志大小 5M
46             'backupCount': 5,
47             'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
48         },
49         'collect': {
50             'level': 'DEBUG',
51             'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
52             'formatter': 'simple',
53             'filename': COLLECT_PATH,  # 日志文件
54             'maxBytes': 1024*1024*5,  # 日志大小 5M
55             'backupCount': 5,
56             'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
57         },
58     },
59     'loggers': {
60         '': {
61             'handlers': ['default', 'console','collect'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
62             'level': 'DEBUG',
63             'propagate': False,  # 向上(更高level的logger)传递
64         },
65     },
66 }
logging配置文件

loggers的子字典中key表示logger名,若将key设置为空key,这样在生成logger对象时,使用logging.getLogger(__name__),不同文件会产生相应的logger名,可以使得日志输出时标识不同。

'loggers': {
        '': {
            'handlers': ['default', 'console'], 
            'level': 'DEBUG',
            'propagate': True, 
        },
}

导入logging的配置文件后,定义log对象生成的函数。

from conf import settings
import logging.config

def logger_handle(log_name):
    logging.config.dictConfig(settings.LOGGING_DIC)
    logger = logging.getLogger(log_name)
    return logger

在需要记录log的文件中导入上述文件。

from lib import common
def test():
    print("支付成功")
    log_msg = "xxx在商城消费8元"
    logger = common.logger_handle("支付日志")
    logger.info(log_msg)

 四、re模块

正则是用来描述一类实物的规则,主要用于匹配字符串。

import re
#\w和\W
print(re.findall('\w',"asdf 1234 %@^#"))
print(re.findall('\W',"asdf 1234 %@^#"))
#\s和\S,\n和\t都可被\s匹配
print(re.findall('\s',"asdf 1234 %@^#"))
print(re.findall('\S',"asdf 1234 %@^#"))
print(re.findall('\s',"asdf 1234\n\t %@^#"))
#\d和\D
print(re.findall('\d',"asdf 1234 %@^#"))
print(re.findall('\D',"asdf 1234 %@^#"))
#^和$
print(re.findall('^a',"asdf 1234 %@^#"))
print(re.findall('#$',"asdf 1234 %@^#"))
#匹配重复
print(re.findall('a.d',"asdf 1234 %@^#"))
print(re.findall('as*',"asdf ass 1234 %@^#"))
print(re.findall('as?',"asdf ass 1234 %@^#"))
print(re.findall('a.*',"asdf ass 1234 %@^#"))
print(re.findall('a.*?',"asdf ass 1234 %@^#")) #.*默认贪婪匹配,.*?为非贪婪匹配
print(re.findall('as+',"asdf ass 1234 %@^#"))
print(re.findall('as{0,2}',"asdf ass 1234 %@^#"))
print(re.findall('a[cs%]d',"asdf acd 1234  a%d@^#")) #[]内都为普通字符,如果-没有转义,需要放到[]的首尾处
#分组
print(re.findall('a(\w+)',"asdf ass 1234 %@^#")) #只输出括号中的
print(re.findall('a(?:\w+)',"asdf ass 1234 %@^#")) #输出匹配到的全部内容

re模块相关的方法

findall返回所有满足条件的结果,放在列表中

import re
print(re.findall('\d\s',"asdf a2 1234 %@^#"))

search只找到第一个匹配然后返回包含匹配结果的对象,通过group()来获取匹配的字符串,如果没有匹配到内容,返回None

import re
print(re.search('\d\s',"asdf a2 1234 %@^#").group())

match同search,只不过是从字符串开始匹配

import re
print(re.search('^a',"asdf a2 1234 %@^#").group())

split按照匹配到的字符进行字符串分割。

import re
print(re.split('[ab]','abcd'))#从开头按照a分割为''和'bcd',在按照b分割为''和'cd'

sub,将字符串中匹配的到子串替换为新的字符串

import re
print(re.sub('a','A','aabbccad')) #不指定n,默认替换所有
print(re.sub('a','A','aabbccad',1)) #只替换第一个
print(re.sub('^(\w)(.*?)(\w)$',r'\3\2\1','aabbccad',1)) #加括号后自动编序号,按照序号交换收尾字符

compile,用正则表达式生成一个对象,每次使用时通过对象调用相关方法

import re
obj=re.compile("(?:\d{0,3}\.){3}\d{0,3}")
print(obj.findall("ipaddr:10.1.1.1\n"))
print(obj.findall("src_ip:192.168.1.1"))

五、time和datetime模块

python中的几个时间:

  a) 时间戳,表示从1970年1月1日00:00:00开始按照秒计算的偏移量。可使用time.time查看当前时间戳。

  b)格式化的时间字符串

  c)结构化的时间:struct_time元组工9个元素,分别是年、月、日

import time
print(time.time())  #时间戳
print(time.strftime("%Y-%m-%d %X")) #格式化时间
print(time.localtime()) #本地时区结构化时间
print(time.gmtime())   #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.     
%d    Day of the month as a decimal number [01,31].     
%H    Hour (24-hour clock) as a decimal number [00,23].     
%I    Hour (12-hour clock) as a decimal number [01,12].     
%j    Day of the year as a decimal number [001,366].     
%m    Month as a decimal number [01,12].     
%M    Minute as a decimal number [00,59].     
%p    Locale’s equivalent of either AM or PM.    (1)
%S    Second as a decimal number [00,61].    (2)
%U    Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0.    (3)
%w    Weekday as a decimal number [0(Sunday),6].     
%W    Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0.    (3)
%x    Locale’s appropriate date representation.     
%X    Locale’s appropriate time representation.     
%y    Year without century as a decimal number [00,99].     
%Y    Year with century as a decimal number.     
%z    Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].     
%Z    Time zone name (no characters if no time zone exists).     
%%    A literal '%' character.

格式化字符串的时间格式
格式化时间字段含义

三种时间直接转换关关系

 

import time
print(time.localtime())  #获取本地时间
print(time.localtime(13333333))  #时间戳转换为机构化时间
print(time.mktime(time.localtime()))  #结构化时间转换为时间戳
print(time.strftime("%Y-%m-%d %X",time.localtime())) #结构化时间转换为格式化时间
print(time.strptime("2018-01-09 21:15:00","%Y-%m-%d %X")) #格式化时间转换为结构化时间

 

posted @ 2018-01-11 23:05  n1ghtwatcher  阅读(257)  评论(0编辑  收藏  举报