常用模块

模块优点

  • 提高可维护
  • 可重用
  • 避免函数名和变量名的冲突

模块一旦被调用,相当于执行了 .py 文件

国内豆瓣源:

pip3 install -i http://pypi.douban.com/simple/  xxx模块名 --trusted-host pypi.douban.com

包(Package)

一个文件夹管理多个模块文件,这个文件夹就被成为包。

模块导入:

import sys,os
BASE_PATH = os.path.dirname(os.path.abspath(__file__))  # 获取绝对路径abspath
sys.path.append(BASE_PATH)

相对导入

from . import main  # .代表当前文件所在的路径
from ..xxx import main  # ..代表上一层

文件夹被python解释器视作package需要满足两个条件:

  1. 文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件。
  2. 不能作为顶层模块来执行该文件夹中的py文件(即不能作为主函数的入口)。

time & datetime 模块

time

  • time.localtime([secs]):将一个时间戳转换为当前时区的struct_time。secs参数未提供,则以当前时间为准。

  • time.gmtime([secs]):和localtime()方法类似,gmtime()方法是将一个时间戳转换为UTC时区(0时区)的struct_time。

  • time.time():返回当前时间的时间戳。

  • time.mktime(t):将一个struct_time转化为时间戳。

  • time.sleep(secs):线程推迟指定的时间运行。单位为秒。

  • time.asctime([t]):把一个表示时间的元组或者struct_time表示为这种形式:'Sun Oct 1 12:04:38 2017'。如果没有参数,将会将time.localtime()作为参数传入。

  • time.ctime([secs]):把一个时间戳(按秒计算的浮点数)转化为time.asctime()的形式。如果参数未给或者为None的时候,将会默认time.time()为参数。它的作用相当于time.asctime(time.localtime(secs))。

  • time.strftime(format[, t]):把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime()返回)转化为格式化的时间字符串。如果t未指定,将传入time.localtime()。

    time.strftime("%Y-%m-%d %X", time.localtime()) #输出'2017-10-01 12:14:23'
    
  • time.strptime(string[, format]):把一个格式化时间字符串转化为struct_time。实际上它和strftime()是逆操作。

    time.strptime('2017-10-3 17:54',"%Y-%m-%d %H:%M") #输出 time.struct_time(tm_year=2017, tm_mon=10, tm_mday=3, tm_hour=17, tm_min=54, tm_sec=0, tm_wday=1, tm_yday=276, tm_isdst=-1)
    

datetime

  1. d=datetime.datetime.now() 返回当前的datetime日期类型
    d.timestamp(),d.today(), d.year,d.timetuple()等方法可以调用

  2. datetime.date.fromtimestamp(322222) 把一个时间戳转为datetime日期类型

  3. 时间运算(只能运算days、hours、minutes、secs)

    >>> datetime.datetime.now()
    
    datetime.datetime(2017, 10, 1, 12, 53, 11, 821218)
    
    >>> datetime.datetime.now() + datetime.timedelta(4) #当前时间 +4天
    
    datetime.datetime(2017, 10, 5, 12, 53, 35, 276589)
    
    >>> datetime.datetime.now() + datetime.timedelta(hours=4) #当前时间+4小时
    
    datetime.datetime(2017, 10, 1, 16, 53, 42, 876275)
    
  4. 时间替换

    >>> d.replace(year=2999,month=11,day=30)
    
    datetime.date(2999, 11, 30)
    

random模块

通过random模块可以很容易生成随机字符串

>>> random.randrange(1,10) #返回1-10之间的一个随机数,不包括10
>>> random.randint(1,10) #返回1-10之间的一个随机数,包括10

>>> random.randrange(0, 100, 2) #随机选取0到100间的偶数

>>> random.random()  #返回一个随机浮点数
>>> random.choice('abce3#$@1') #返回一个给定数据集合中的随机字符
'#'

>>> random.sample('abcdefghij',3)  #从多个字符中选取特定数量的字符
['a', 'd', 'b']

#生成随机字符串
>>> import string 
>>> ''.join(random.sample(string.ascii_lowercase + string.digits, 6)) 
'4fvda1'

#洗牌
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> random.shuffle(a)
>>> a
[3, 0, 7, 2, 1, 6, 5, 8, 9, 4]

os模块

得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd()
返回指定目录下的所有文件和目录名:os.listdir()
函数用来删除一个文件:os.remove()
删除多个目录:os.removedirs(r“c:python”)
检验给出的路径是否是一个文件:os.path.isfile()
检验给出的路径是否是一个目录:os.path.isdir()
判断是否是绝对路径:os.path.isabs()
检验给出的路径是否真地存:os.path.exists()
返回一个路径的目录名和文件名:os.path.split()     e.g os.path.split('/home/swaroop/byte/code/poem.txt') 结果:('/home/swaroop/byte/code', 'poem.txt') 
分离扩展名:os.path.splitext()       e.g  os.path.splitext('/usr/local/test.py')    结果:('/usr/local/test', '.py')
获取路径名:os.path.dirname()
获得绝对路径: os.path.abspath()  
获取文件名:os.path.basename()
运行shell命令: os.system()
读取操作系统环境变量HOME的值:os.getenv("HOME") 
返回操作系统所有的环境变量: os.environ 
设置系统环境变量,仅程序运行时有效:os.environ.setdefault('HOME','/home/alex')
给出当前平台使用的行终止符:os.linesep    Windows使用'
',Linux and MAC使用'
'
指示你正在使用的平台:os.name       对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix'
重命名:os.rename(old, new)
创建多级目录:os.makedirs(r“c:python	est”)
创建单个目录:os.mkdir(“test”)
获取文件属性:os.stat(file)
修改文件权限与时间戳:os.chmod(file)
获取文件大小:os.path.getsize(filename)
结合目录名与文件名:os.path.join(dir,filename)
改变工作目录到dirname: os.chdir(dirname)
获取当前终端的大小: os.get_terminal_size()
杀死进程: os.kill(10884,signal.SIGKILL)

sys模块

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
sys.stdout.write('please:')  # 标准输出 , 引出进度条的例子, 注,在py3上不行,可以用print代替
val = sys.stdin.readline()[:-1] # 标准输入
sys.getrecursionlimit() # 获取最大递归层数
sys.setrecursionlimit(1200) # 设置最大递归层数
sys.getdefaultencoding()  # 获取解释器默认编码
sys.getfilesystemencoding  # 获取内存数据存到文件里的默认编码

shutil模块

高级的 文件、文件夹、压缩包 处理模块

shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中

import shutil
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

shutil.copyfile(src, dst)
拷贝文件

shutil.copyfile('f1.log', 'f2.log')  # 目标文件无需存在

shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变

shutil.copymode('f1.log', 'f2.log')  # 目标文件必须存在

shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags

shutil.copystat('f1.log', 'f2.log')  # 目标文件必须存在

shutil.copy(src, dst)
拷贝文件和权限

shutil.copy('f1.log', 'f2.log')

shutil.copy2(src, dst)
拷贝文件和状态信息

shutil.copy2('f1.log', 'f2.log')

shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹

shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))  # 目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除

shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件

shutil.rmtree('folder1')

shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。

shutil.move('folder1', 'folder3')

shutil.make_archive(base_name, format,...)
创建压缩包并返回文件路径,例如:zip、tar

  • base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,如 data_bak =>保存至当前路径、/tmp/data_bak =>保存至/tmp/

  • format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”

  • root_dir: 要压缩的文件夹路径(默认当前目录)

  • owner: 用户,默认当前用户

  • group: 组,默认当前组

  • logger: 用于记录日志,通常是logging.Logger对象

# 将 /data 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')

# 将 /data下的文件打包放置 /tmp/目录
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')

shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
zipfile压缩&解压缩

import zipfile

# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解压
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path='.')
z.close()

tarfile压缩&解压缩

import tarfile

# 压缩
>>> t=tarfile.open('/tmp/egon.tar','w')
>>> t.add('/test1/a.py',arcname='a.bak')
>>> t.add('/test1/b.py',arcname='b.bak')
>>> t.close()

# 解压
>>> t=tarfile.open('/tmp/egon.tar','r')
>>> t.extractall('/egon')
>>> t.close()

序列化模块 json&pickle

  • json,用于字符串 和 python数据类型间进行转换
  • pickle,用于python特有的类型 和 python的数据类型间进行转换
import pickle
data = {'k1':123,'k2':'Hello'}

# pickle.dumps 将数据通过特殊的形式转换位只有python语言认识的字符串
p_str = pickle.dumps(data)
print(p_str)

#pickle.dump 将数据通过特殊的形式转换位只有python语言认识的字符串,并写入文件
with open('D:/result.pk','wb',encoding='utf8') as fp:
    pickle.dump(data,fp)

import json
# json.dumps 将数据通过特殊的形式转换位所有程序语言都认识的字符串
j_str = json.dumps(data)
print(j_str)

#pickle.dump 将数据通过特殊的形式转换位只有python语言认识的字符串,并写入文件
with open('D:/result.json','wb',encoding='utf8') as fp:
    json.dump(data,fp)

JSON:

优点:跨语言、体积小

缺点:只能支持intstrlist upledict

Pickle:

优点:专为python设计,支持python所有的数据类型

缺点:只能在python中使用,存储数据占空间大

shelve模块

shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式。python独有

# 序列化
import shelve

f = shelve.open('shelve_test')  # 打开一个文件

names = ["alex", "rain", "test"]
info = {'name':'alex','age':22}

f["names"] = names  # 持久化列表
f['info_dic'] = info

f.close()
d = shelve.open('shelve_test')  # 打开一个文件

print(d['names'])
print(d['info_dic'])

#del d['test'] # 还可以删除

xml模块

xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用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>
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) #打印生成的格式

configparser模块

此模块用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser。

常见配置文件格式如下

[DEFAULT]
ServerAliveInterval = 45   
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no
>>> import configparser # 导入模块
>>> config = configparser.ConfigParser()  # 实例化(生成对象)
>>> config.sections()  # 调用sections方法
[]
>>> config.read('example.ini')  # 读配置文件(注意文件路径)
['example.ini']
>>> config.sections() # 调用sections方法(默认不会读取default)
['bitbucket.org', 'topsecret.server.com']
>>> 'bitbucket.org' in config # 判断元素是否在sections列表内
True
>>> 'bytebong.com' in config
False
>>> config['bitbucket.org']['User'] # 通过字典的形式取值
'hg'
>>> config['DEFAULT']['Compression']
'yes'
>>> topsecret = config['topsecret.server.com']
>>> topsecret['ForwardX11']
'no'
>>> topsecret['Port']
'50022'
>>> for key in config['bitbucket.org']: print(key) # for循环 bitbucket.org 字典的key
...
user
compressionlevel
serveraliveinterval
compression
forwardx11
>>> config['bitbucket.org']['ForwardX11']
'yes'

其它增删改查语法

[group1] # 支持的两种分隔符“=”, “:”
k1 = v1
k2:v2

[group2]
k1 = v1

import ConfigParser

config = ConfigParser.ConfigParser()
config.read('i.cfg')

# ########## 读 ##########
# secs = config.sections()
# print(secs)
# options = config.options('group2') # 获取指定section的keys
# print(options)

# item_list = config.items('group2') # 获取指定 section 的 keys & values ,key value 以元组的形式
# print(item_list)

# val = config.get('group1','key') # 获取指定的key 的value
# val = config.getint('group1','key')

# ########## 改写 ##########
# sec = config.remove_section('group1') # 删除section 并返回状态(true, false)
# config.write(open('i.cfg', "w")) # 对应的删除操作要写入文件才会生效

# sec = config.has_section('wupeiqi')
# sec = config.add_section('wupeiqi')
# config.write(open('i.cfg', "w")) # 


# config.set('group2','k1',11111)
# config.write(open('i.cfg', "w"))

# config.remove_option('group2','age')
# config.write(open('i.cfg', "w"))

hashlib模块

import hashlib

m = hashlib.md5()
m.update(b"Hello")
m.update(b"It's me")
print(m.digest())
m.update(b"It's been a long time since last time we ...")

print(m.digest()) #2进制格式hash
print(len(m.hexdigest())) #16进制格式hash
'''
def digest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of binary data. """
    pass

def hexdigest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of hexadecimal digits. """
    pass

'''
import hashlib

# ######## md5 ########

hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())

# ######## sha1 ########

hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())

# ######## sha256 ########

hash = hashlib.sha256()
hash.update('admin')
print(hash.hexdigest())


# ######## sha384 ########

hash = hashlib.sha384()
hash.update('admin')
print(hash.hexdigest())

# ######## sha512 ########

hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())

subprocess模块

  • subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推荐
  • subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面实现的内容差不多,另一种写法
  • subprocess.Popen() #上面各种方法的底层封装

run( )方法

标准写法

subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)

涉及到管道|的命令需要这样写

subprocess.run('df -h|grep disk1',shell=True) # shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析

call( )方法

# 执行命令,返回命令执行状态 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])

# 执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0

# 接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果 
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')

# 接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

# 执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0
drwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM
'

Popen( )方法

常用参数:

  • args:shell命令,可以是字符串或者序列类型(如:list,元组)
  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
  • shell:同上
  • cwd:用于设置子进程的当前目录
  • env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

logging模块

Level When it’s used
DEBUG Detailed information, typically of interest only when diagnosing problems.
INFO Confirmation that things are working as expected.
WARNING An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected.
ERROR Due to a more serious problem, the software has not been able to perform some function.
CRITICAL A serious error, indicating that the program itself may be unable to continue running.

自定义日志格式

%(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提供了应用程序可以直接使用的接口;
  • handler将(logger创建的)日志记录发送到合适的目的输出;
  • filter提供了细度设备来决定输出哪条日志记录;
  • formatter决定日志记录的最终输出格式。
# Author:q1.ang
import logging

class IgnoreBackupLogFilter(logging.Filter):
    """忽略带db backup 的日志"""
    def filter(self, record): #固定写法
        return "db backup" not in record.getMessage()

# 生成 logger 对象
logger = logging.getLogger('web')
logger.setLevel(logging.INFO)  # 默认级别为 WARNING

# 把filter对象添加到logger中
logger.addFilter(IgnoreBackupLogFilter())

# 生成 handler 对象
ch = logging.StreamHandler()
fh = logging.FileHandler('web.log')

# 把handler对象绑定到logger
logger.addHandler(ch)
logger.addHandler(fh)

# 生成formatter对象
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(lineno)s - %(message)s')

# 把formatter对象绑定handler对象
ch.setFormatter(console_formatter)
ch.setLevel(logging.DEBUG)

fh.setFormatter(file_formatter)
fh.setLevel(logging.WARNING)

logger.debug('this is debug')
logger.info('info')
logger.warning('..')
logger.error('hehe')

re模块

常用的表达式规则

'.'     默认匹配除
之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^'     匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","
abc
eee",flags=re.MULTILINE)
'$'     匹配字符结尾, 若指定flags MULTILINE ,re.search('foo.$','foo1
foo2
',re.MULTILINE).group() 会匹配到foo1
'*'     匹配*号前的字符0次或多次, re.search('a*','aaaabac')  结果'aaaa'
'+'     匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?'     匹配前一个字符1次或0次 ,re.search('b?','alex').group() 匹配b 0次
'{m}'   匹配前一个字符m次 ,re.search('b{3}','alexbbbs').group()  匹配到'bbb'
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|'     匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配, re.search("(abc){2}a(123|45)", "abcabca456c").group() 结果为'abcabca45'


'A'    只从字符开头匹配,re.search("Aabc","alexabc") 是匹配不到的,相当于re.match('abc',"alexabc") 或^
''    匹配字符结尾,同$ 
'd'    匹配数字0-9
'D'    匹配非数字
'w'    匹配[A-Za-z0-9]
'W'    匹配非[A-Za-z0-9]
's'     匹配空白字符、	、
、
 , re.search("s+","ab	c1
3").group() 结果 '	'

'(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}

re的匹配语法

  • re.match 从头开始匹配,第一个值不匹配就返回None
  • re.search 匹配包含,只返回第一个匹配的值,取值:re.search('[0-9]',a).group()
  • re.findall 把所有匹配到的字符放到以列表中的元素返回
  • re.split 以匹配到的字符当做列表分隔符
  • re.sub 匹配字符并替换
  • re.fullmatch 全部匹配

Flags标志符

  • re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
  • M(MULTILINE): 多行模式,改变'^'和'$'的行为
  • S(DOTALL): 改变'.'的行为,make the '.' special character match any character at all, including a newline; without this flag, '.' will match anything except a newline.
  • X(re.VERBOSE) 可以给你的表达式写注释,使其更可读,下面这2个意思一样
原文地址:https://www.cnblogs.com/q1ang/p/9497880.html