3.4-模块与包
模块与包
模块
re模块
什么是正则
正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说,正则就是用来描述一类事物的规则。
在python中,正则内嵌在python中,通过re模块实现,正则表达式模式被编译成一系列的字节码,然后用C编写的匹配引擎执行;
元字符 匹配内容
\w 匹配字母(包含中文)或数字或下划线
\W 匹配非字母(包含中文)或数字或下划线
\s 匹配任意的空白符
\S 匹配任意非空白符
\d 匹配数字
\D 匹配非数字
\A 从字符串开头匹配
\Z 从结尾开始匹配
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配字符串的开始
$ 匹配字符串的结尾
. 匹配任意字符,除了换行符。当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中的字符的所有字符
* 匹配0个或者多个左边的字符
+ 匹配一个或者多个左边的字符
? 匹配0个或者1个左边的字符,非贪婪方式
{n} 精准匹配n个前面的表达式
{n,m} 匹配n到m次由前面的正则表达式定义的片段,贪婪方式
a|b 匹配a或者b
() 匹配括号内的表达式,也表示一个组
匹配模式举例
# ----------------匹配模式--------------------
# 1,之前学过的字符串的常用操作:一对一匹配
s1 = 'abcdef黑色利穆'
print(s1.find('黑色')) # 6
# 正则匹配:
# 单个字符匹配
import re
# \w 与 \W
print(re.findall('\w', '黑色利穆ab 123*() _')) # ['黑', '色', '利', '穆', 'a', 'b', '1', '2', '3', '_']
print(re.findall('\W', '黑色利穆ab 123*() _')) # [' ', '*', '(', ')', ' ']
# \s 与\S
print(re.findall('\s','黑色利穆hslm*(_ \t \n')) # [' ', '\t', ' ', '\n']
print(re.findall('\S','黑色利穆hslm*(_ \t \n')) # ['黑', '色', '利', '穆', 'h', 's', 'l', 'm', '*', '(', '_']
# \d 与 \D
print(re.findall('\d','1234567890 hslm *(_')) # ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
print(re.findall('\D','1234567890 hslm *(_')) # [' ', 'h', 's', 'l', 'm', ' ', '*', '(', '_']
# \A 与 ^
print(re.findall('\Ahel', 'hello 黑色利穆 ---777')) # ['hel']
print(re.findall('^hel', 'hello 黑色利穆 ---777')) # ['hel']
# \Z 与 $
print(re.findall('666\Z','hello 黑色利穆 *-_-* \n666')) # ['666']
print(re.findall('666$','hello 黑色利穆 *-_-* \n666')) # ['666']
# \n 与 \t
print(re.findall('\n','hello \n 黑色利穆 \t*-_-*\t \n666')) # ['\n', '\n']
print(re.findall('\t','hello \n 黑色利穆 \t*-_-*\t \n666')) # ['\t', '\t']
# 元字符匹配
# 重复匹配
# . ? * + {m,n} .* .*?
# . 匹配任意字符,除了换行符(re.DOTALL 这个参数可以匹配\n)
# 如果匹配成功,光标则移动到匹配成功的最后的字符;
# 如果匹配未成功,光标则向下移动一位,再次进行匹配;
print(re.findall('a.b', 'ab aab a*b a2b a牛b a\nb')) # ['aab', 'a*b', 'a2b', 'a牛b']
print(re.findall('a.b', 'ab aab a*b a2b a牛b a\nb',re.DOTALL)) # ['aab', 'a*b', 'a2b', 'a牛b', 'a\nb']
# ?匹配0个或者1个由左边字符定义的片段。
print(re.findall('a?b', 'ab aab abb aaaab a牛b aba**b')) # ['ab', 'ab', 'ab', 'b', 'ab', 'b', 'ab', 'b']
# * 匹配0个或者多个左边字符表达式。 满足贪婪匹配
print(re.findall('a*b', 'ab aab aaab abbb')) # ['ab', 'aab', 'aaab', 'ab', 'b', 'b']
print(re.findall('ab*', 'ab aab aaab abbbbb')) # ['ab', 'a', 'ab', 'a', 'a', 'ab', 'abbbbb']
# + 匹配1个或者多个左边字符表达式。 满足贪婪匹配
print(re.findall('a+b', 'ab aab aaab abbb')) # ['ab', 'aab', 'aaab', 'ab']
# {m,n} 匹配m个至n(n能取到)个左边字符表达式。 满足贪婪匹配
print(re.findall('a{2,4}b', 'ab aab aaab aaaaabb')) # ['aab', 'aaab', 'aaaab']
# .* 贪婪匹配 从头到尾.
print(re.findall('a.*b', 'ab aab a*()b')) # ['ab aab a*()b']
# 非贪婪匹配
# .*? 此时的?不是对左边的字符进行0次或者1次的匹配,
# 而只是针对.*这种贪婪匹配的模式进行一种限定:告知他要遵从非贪婪匹配 推荐使用!
print(re.findall('a.*?b', 'ab a1b a*()b, aaaaaab')) # ['ab', 'a1b', 'a*()b', 'aaaaaab']
# []: 一个中括号代表一个字符
# - 在[]中表示范围,如果想要匹配上- 那么这个-符号不能放在中间.
# ^ 在[]中表示取反的意思.
print(re.findall('a.b', 'a1b a3b aeb a*b arb a_b')) # ['a1b', 'a3b', 'a4b', 'a*b', 'arb', 'a_b']
print(re.findall('a[abc]b', 'aab abb acb adb afb a_b')) # ['aab', 'abb', 'acb']
print(re.findall('a[0-9]b', 'a1b a3b aeb a*b arb a_b')) # ['a1b', 'a3b']
print(re.findall('a[a-z]b', 'a1b a3b aeb a*b arb a_b')) # ['aeb', 'arb']
print(re.findall('a[a-zA-Z]b', 'aAb aWb aeb a*b arb a_b')) # ['aAb', 'aWb', 'aeb', 'arb']
print(re.findall('a[0-9][0-9]b', 'a11b a12b a34b a*b arb a_b')) # ['a11b', 'a12b', 'a34b']
print(re.findall('a[*-+]b','a-b a*b a+b a/b a6b')) # ['a*b', 'a+b']
# - 在[]中表示范围,如果想要匹配上- 那么这个-符号不能放在中间.
print(re.findall('a[-*+]b','a-b a*b a+b a/b a6b')) # ['a-b', 'a*b', 'a+b']
# 分组:
# () 制定一个规则,将满足规则的结果匹配出来
print(re.findall('([a-z]+)_nb', 'xiaoming_nb, xiaohong_nb, xiaogang_nb'))
# ['xiaoming', 'xiaohong', 'xiaogang']
# 应用举例:
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))#['http://www.baidu.com']
# | 匹配 左边或者右边
print(re.findall('aaa|bbb|黑色', '我说黑色的aaa和红色的bbb还有黑色的ccc'))
# ['黑色', 'aaa', 'bbb', '黑色']
print(re.findall('compan(y|ies)','Too many companies have gone bankrupt, and the next one is my company')) # ['ies', 'y']
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company')) # ['companies', 'company']
# 分组() 中加入?: 表示将整体匹配出来而不只是()里面的内容。
常用方法举例
import re
# findall 全部找到返回一个列表
print(re.findall('([a-z]+)_nb', 'xiaoming_nb, xiaohong_nb, xiaogang_nb'))
# search 找到第一个匹配,然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串:
print(re.search('([a-z]+)_nb', 'xiaoming_nb, xiaohong_nb, xiaogang_nb'))
# <_sre.SRE_Match object; span=(0, 11), match='xiaoming_nb'>
print(re.search('([a-z]+)_nb', 'xiaoming_nb, xiaohong_nb, xiaogang_nb').group())
# xiaoming_nb
# match 从字符串开头匹配,如果以符合条件的字符串开头则返回,否则返回None
print(re.match('hslm', 'ahslmisman')) # None
print(re.match('hslm', 'hslmisman').group()) # hslm
# split 分割,按照任意分割字符进行分割
s1 = 'hslm;xm,xw-xg.zhangsan'
print(re.split('[- ; , .]', s1))
# ['hslm', 'xm', 'xw', 'xg', 'zhangsan']
print(re.split('x', s1))
# ['hslm;', 'm,', 'w-', 'g.zhangsan']
# sub 替换
s = '在这个世界上的某个角落,有一个叫做小明的男人,他英俊潇洒风流倜傥。小明真是完美'
print(re.sub('小明', '黑色利穆', s))
# 在这个世界上的某个角落,有一个叫做黑色利穆的男人,他英俊潇洒风流倜傥。黑色利穆真是完美
# compile 制定规则
obj = re.compile('\d{2}')
print(obj.search('aaabbbccc111222333').group()) # 11
print(obj.findall('aaabbbccc111222333')) # ['11', '12', '22', '33']
# finditer 返回一个迭代器
ret = re.finditer('\d', '123abc4') # 返回一个存放匹配结果的迭代器
print(ret) # <callable_iterator object at 0x000001AFE0D70C88>
print(next(ret).group()) # 1 查看第一个结果
for i in ret: # for循环查看所有结果
print(i.group())
# 2
# 3
# 4
包
什么是包
包就是含有一个__init__.py 文件的文件夹,创建包的目的就是为了用文件夹将文件/模块组织起来;
在python3中,即使包下没有__init__.py,import包仍然不会报错。在python2中,包下一定要有该文件;
创建包的目的不是为了运行,而是被导入使用。包只是模块的一种形式。包的本质就是一种模块;
为何要使用包,注意事项
包的本质就是一个文件夹,文件夹的唯一功能就是将文件组织起来;
随着功能越写越多,无法将功能都放在一个文件中,于是要使用模块去组织功能,随着模块越来越多,就需要用文件夹将模块文件组织起来,以提高程序的结构性和可维护性;
注意事项:
- 关于包相关的导入语句也分为import和 from ... import .. 两种。无论哪种,无论在什么位置,导入时必须遵循一个原则:凡是在导入时带点的,点的左边必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但是必须遵循这个原则。但对于导入后,就没有这种限制。点的左边可以是包,模块,函数,类;(都是用点的方式调用自己的属性);
- import导入文件时,产生名称空间中的名字来源于文件,import包,产生的名称空间的名字同样来源于文件,即包下的init.py文件,导入包的本质就是导入该文件;
- 包A和包B下有同名模块也不会冲突,如A.a与B.a来自两个命名空间;
包的使用
# 实例文件目录
glance
__init__.py
api
__init__.py
policy.py
versions.py
cmd
__init__.py
manage.py
db
__init__.py
models.py
start.py
# 文件内容
# policy.py
def get():
print('from policy.py')
# versions.py
def create_resource(conf):
print('from version.py: ',conf)
# manage.py
def main():
print('from manage.py')
# models.py
def register_models(engine):
print('from models.py: ', engine)
执行文件与示范文件在统计目录下:
包的使用-- import
# glance/start.py
import glance.db.models
glance.db.models.register_models('mysql')
# from models.py: mysql
# 单独导入包名称时不会导入包中所有包含的所有子模块:
# 执行报错:
# start.py
import glance
glance.cmd.manage.main()
# AttributeError: module 'glance' has no attribute 'cmd'
# 解决方法:
# glance/__init__.py
from . import cmd
# glance/cmd/__init__.py
from . import manage
# glance/start.py
import glance
glance.cmd.manage.main()
# from manage.py
包的使用 from ... import ...
注意:from后import导入的模块,不能带点,否则语法错误,如:from a import b.c(语法错误)
# glance/start.py
from glance.db import models
models.register_models('mysql')
# from models.py: mysql
from glance.db.models import register_models
register_models('mysql')
# from models.py: mysql
从一个模块内导入所有 *
从一个包导入所有 *
将api中导入所有,实际上 from glance.api import * 只会导入包api下__init__.py文件中定义的名字,可以在这个文件中定义__all__
# glance/api/__init__.py
x = 10
def func():
print('from api.__init__.py')
__all__ = ['x', 'func', 'policy']
# glance/__init__.py
from glance.api import *
# 此时执行from glance.api import * 就导入 __all__中的内容(version任然不能导入)
# 处理好所有包的导入
# glance/__init__.py
from .api.policy import get
from .api.versions import create_resource
from .cmd.manage import main
from .db.models import register_models
__all__ = ['get', 'create_resource', 'main', 'register_models']
# glance/start.py
from glance import *
get()
create_resource('a.conf')
main()
register_models('mysql')
# from policy.py
# from version.py: a.conf
# from manage.py
# from models.py: mys
绝对导入和相对导入
最顶级包glance是给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时有绝对导入和相对导入两种方式:
- 绝对导入:以glance作为开始;
- 相对导入:用.或者..的方式为起始(只能在一个包中使用,不能用于不同目录中);
举例:在glance/api/version.py中导入glance/cmd/manage.py
# glance/api/versions.py
# 绝对导入
from glance.cmd import manage
manage.main()
# from manage.py
# 相对导入
from ..cmd import manage
manage.main()
# glance/start.py
from glance.api import versions
# from manage.py
包以及包所包含的模块都是用来被导入的,而不是直接执行的。而环境变量都是以执行文件为准的。
例子:想在glance/api/version.py中导入glance/api/policy.py,一看这俩模块在同一个目录下:
import policy
policy.get()
# from policy.py
# 这样单独运行version.py没有问题。运行version.py的路径搜搜就是从当前路径开始的,于是在导入policy时能在当前目录下找到;
# 但是:子包中的模块version.py极有可能被一个glance包同一级别的其他文件导入。比如在glance同级下的start.py文件中导入version.py
from glance.api import versions
# ModuleNotFoundError: No module named 'policy'
# 执行结果报错:分析
# 此时导入version在version.py中执行;
# import policy需要从sys.paht也就是当前目录找policy.py
# 肯定找不到;
总结:
绝对导入和相对导入:
绝对导入:以执行文件的sys.path为起始点开始导入,称之为绝对导入;
优点:执行文件与被导入的模块中都可以使用;
缺点:所有导入都是以sys.path为起始点,导入麻烦;
相对导入:参照当前所在文件的文件夹为起始开始查找,称之为相对导入;
符号:. 代表当前所在文件夹, .. 代表上一级的上一级文件夹;
优点:导入更加简单;
缺点:只能在导入包中的模块时才能使用;
ps:
1:相对导入只能用于包内部模块之间的互相导入,导入者与被导入者都必须存在于一个保内;
2:试图在顶级宝之外使用相对导入是错误的;必须在顶级包内使用相对导入,每增加一个.代表跳到上一级文件夹,而上一级不应该超出顶级包;