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中,包下一定要有该文件;
创建包的目的不是为了运行,而是被导入使用。包只是模块的一种形式。包的本质就是一种模块;

为何要使用包,注意事项

包的本质就是一个文件夹,文件夹的唯一功能就是将文件组织起来;

随着功能越写越多,无法将功能都放在一个文件中,于是要使用模块去组织功能,随着模块越来越多,就需要用文件夹将模块文件组织起来,以提高程序的结构性和可维护性;

注意事项:

  1. 关于包相关的导入语句也分为import和 from ... import .. 两种。无论哪种,无论在什么位置,导入时必须遵循一个原则:凡是在导入时带点的,点的左边必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但是必须遵循这个原则。但对于导入后,就没有这种限制。点的左边可以是包,模块,函数,类;(都是用点的方式调用自己的属性);
  2. import导入文件时,产生名称空间中的名字来源于文件,import包,产生的名称空间的名字同样来源于文件,即包下的init.py文件,导入包的本质就是导入该文件;
  3. 包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包内部也会有彼此之间互相导入的需求,这时有绝对导入和相对导入两种方式:

  1. 绝对导入:以glance作为开始;
  2. 相对导入:用.或者..的方式为起始(只能在一个包中使用,不能用于不同目录中);

举例:在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:试图在顶级宝之外使用相对导入是错误的;必须在顶级包内使用相对导入,每增加一个.代表跳到上一级文件夹,而上一级不应该超出顶级包;
posted @ 2020-12-04 15:09  黑色利穆  阅读(71)  评论(0编辑  收藏  举报