《Python基础教程(第3版)》笔记:第10章模块之re

《Python基础教程(第3版)》笔记:第10章模块之re

10.1 模块

10.1.1 模块就是程序

任何Python程序都可作为模块导入。文件的存储位置很重要,要告诉解释器去哪里查找这个模块,可执行如下命令

>>> import sys 
>>> sys.path.append('C:/python') 

注意 当你导入模块时,可能发现其所在目录中除源代码文件外,还新建了一个名为__pycache\__的子目录。这个目录包含处理后的文件,Python能够更高效地处理它们。以后再导入这个模块时,如果.py文件未发生变化,Python将导入处理后的文件,否则将重新生成处理后的文件。删除目录__pycache__不会有任何害处,因为必要时会重新创建它。


模块并不是用来执行操作(如打印文本)的,而是用于定义变量、函数、类等。鉴于定义只需做一次,因此导入模块多次和导入一次的效果相同。

要重新加载模块,可使用模块importlib中的函数reload,它接受一个参数(要重新加载的模块),并返回重新加载的模块。如果在程序运行时修改了模块,并希望这种修改反映到程序中,这将很有用。

10.1.2 模块是用来下定义的

模块在首次被导入程序时执行。模块像类一样,有自己的作用域。这意味着在模块中定义的类和函数以及对其进行赋值的变量都将成为模块的属性。

  1. 模块中定义函数

提示 像处理模块那样,让程序(这意味着将被执行,而不是用作模块)可用后,可使用Python解释器开关-m来执行它。如果随其他模块一起安装了文件progname.py(请注意扩展名),命令python -m progname args将使用命令行参数args来执行程序progname。


  1. 在模块中添加测试代码

模块用于定义函数和类等,但在有些情况下,添加一些测试代码来检查情况是否符合预期很有用。

10.1.3 让模块可用
  • 将模块放在正确的位置;
  • 告诉解释器到哪里去查找。
  1. 将模块放在正确的位置

可在模块sys的变量path中找到目录列表(即搜索路径)。

>>> import sys, pprint 
>>> pprint.pprint(sys.path) 
['C:\\Python35\\Lib\\idlelib', 
 'C:\\Python35', 
 'C:\\Python35\\DLLs', 
 'C:\\Python35\\lib', 
 'C:\\Python35\\lib\\plat-win', 
 'C:\\Python35\\lib\\lib-tk', 
 'C:\\Python35\\lib\\site-packages']

提示 如果要打印的数据结构太大,一行容纳不下,可使用模块pprint中的函数pprint。pprint是个卓越的打印函数,能够更妥善地打印输出。


  1. 告诉解释器到哪里去查找

可使用路径配置文件。这些文件的扩展名为.pth,位于一些特殊目录中,包含要添加到sys.path中的目录。有关这方面的详细信息,请参阅有关模块site的标准库文档

10.1.4 包

为组织模块,可将其编组为包(package),包其实就是另一种模块,但有趣的是它们可包含其他模块。模块存储在扩展名为.py的文件中,而包则是一个目录。要被Python视为包,目录必须包含文件__init__.py。如果像普通模块一样导入包,文件__init__.py的内容就将是包的内容。例如,如果有一个名为constants的包,而文件constants/__init__.py包含语句PI = 3.14,就可以像下面这样做:

import constants 
print(constants.PI) 

学习Python编程而言,最有用的文档是“Python库参考手册”,它描述了标准库中的所有模块。在需要获悉一些有关Python的事实时,十有八九能在这里找到。“Python库参考手册”(https://docs.python.org/library)可在线浏览和下载,几个其他的标准文档(如“Python 入门指南”和“Python 语言参考手册”)也是如此。所有的文档都可在Python网站(https://docs.python.org)上找到。

10.2.4 使用源代码

在大多数情况下,前面讨论的探索技巧都够用了。但要真正理解Python语言,可能需要了解一些不阅读源代码就无法了解的事情。事实上,要学习Python,阅读源代码是除动手编写代码外的最佳方式。
实际阅读源代码应该不成问题,但源代码在哪里呢?假设你要阅读标准模块copy的代码,可以在什么地方找到呢?一种办法是像解释器那样通过sys.path来查找,但更快捷的方式是查看模块的特性__file__。

>>> print(copy.__file__) 
C:\Python35\lib\copy.py 

找到了!你可在代码编辑器(如IDLE)中打开文件copy.py,并开始研究其工作原理。如果列出的文件名以.pyc结尾,可打开以.py结尾的相应文件。

10.3 标准库

10.3.8 re
通配符

句点与除换行符外的其它字符都匹配,只与一个字符匹配,称为通配符(wildcard)

对特殊字符进行转义

转义:在特殊字符的前面加上一个反斜杠,为了表示反斜杠,需写两个反斜杠,让解释器对其进行转义,

\\包含两层转义:解释器进行的转义和模块re执行的转义,切记:不要仅仅依赖解释器转义。

字符集

用方括号将子串括起来,例如:[a-zA-Z0-9],字符集只能匹配一个字符。

排除字符集,在开头添加一个字符,例如’[abc] ’

在字符集中,通常无需对这些字符进行转义,但转义也是合法的。

应牢记如下规则。

  • 脱字符(^)位于字符集开头时,除非要将其用作排除运算符,否则必须对其进行转义。换而言之,除非有意为之,否则不要将其放在字符集开头。
  • 同样,对于右方括号(])和连字符(-),要么将其放在字符集开头,要么使用反斜杠对其进行转义。实际上,如果你愿意,也可将连字符放在字符集末尾
二选一和子模式

二选一的特殊字符:管道字符(|)。所需的模式为’python|perl’。

若将其用于模式的一部分,可将这部分(子模式)放在圆括号内,如:‘p(ython|erl)’

可选模式和重复模式

子模式后面加上问号,如:r’(http://)?(www\.)?python\.org’

子模式可重复多次的运算符:

  • (pattern)*:pattern可重复0、1或多次。

  • (pattern)+:pattern可重复1或多次。

  • (pattern){m,n}:模式重复m~n次

字符串的开头和末尾

^ 来指出在字符串的开头与模式匹配

$ 指定在字符串末尾与模式匹配

模块re的功能函数

表10-9 模块re中一些重要的函数

函 数描 述
compile(pattern[, flags])根据包含正则表达式的字符串创建模式对象
search(pattern, string[, flags])在字符串中查找模式
match(pattern, string[, flags])在字符串开头匹配模式
split(pattern, string[, maxsplit=0])根据模式来分割字符串
findall(pattern, string)返回一个列表,其中包含字符串中所有与模式匹配的子串
sub(pat, repl, string[, count=0])将字符串中与模式pat匹配的子串都替换为repl
escape(string)对字符串中所有的正则表达式特殊字符都进行转义

re.search(pat, string)(其中pat是一个使用字符串表示的正则表达式)

等价于

pat.search(string)(其中pat是使用compile创建的模式对象)。

要查找所有的标点符号,可像下面这样做:

>>> pat = '[a-zA-Z]+' 
>>> text = '"Hm... Err -- are you sure?" he said, sounding insecure.' 
>>> re.findall(pat, text) 
['Hm', 'Err', 'are', 'you', 'sure', 'he', 'said', 'sounding', 'insecure'] 
>>> pat = r'[.?\-",]+' 
>>> re.findall(pat, text) 
['"', '...', '--', '?"', ',', '.'] 
3. 匹配对象和编组

在模块re中,查找与模式匹配的子串的函数都在找到时返回MatchObject对象。

表10-10 re匹配对象的重要方法

方 法描 述
group([group1, …])获取与给定子模式(编组)匹配的子串
start([group])返回与给定编组匹配的子串的起始位置
end([group])返回与给定编组匹配的子串的终止位置(与切片一样,不包含终止位置)
span([group])返回与给定编组匹配的子串的起始和终止位置

示例:

>>> m = re.match(r'www\.(.*)\..{3}', 'www.python.org')
>>> m.group(1)
'python'
>>> m.start(1)
4
>>> m.end(1)
10
>>> m.span(1)
(4, 10)

4. 替换中的组号和函数

为利用re.sub的强大功能,最简单的方式是在替代字符串中使用组号。
例如,假设要将
something’替换为’something’,

>>> emphasis_pattern = r'\*([^\*]+)\*'

提示 要让正则表达式更容易理解,一种办法是在调用模块re中的函数时使用标志VERBOSE。这让你能够在模式中添加空白(空格、制表符、换行符等),而re将忽略它们——除非将它放在字符类中或使用反斜杠对其进行转义。在这样的正则表达式中,你还可添加注释。下述代码创建的模式对象与emphasis_pattern等价,但使用了VERBOSE标志:

>>> emphasis_pattern = re.compile(r''' 
    \*         # 起始突出标志——一个星号
    (          # 与要突出的内容匹配的编组的起始位置
    [^\*]+     # 与除星号外的其他字符都匹配
    )          # 编组到此结束
    \*         # 结束突出标志
    ''', re.VERBOSE) 

创建模式后,就可使用re.sub来完成所需的替换了。

>>> re.sub(emphasis_pattern, r'<em>\1</em>', 'Hello, *world*!')
'Hello, <em>world</em>!'

贪婪和非贪婪模式
重复运算符默认是贪婪的,这意味着它们将匹配尽可能多的内容。例如,假设重写了前面的突出程序,在其中使用了如下模式:

>>> emphasis_pattern = r'\*(.+)\*' 

这个模式与以星号打头和结尾的内容匹配。好像很完美,不是吗?但情况并非如此。

>>> re.sub(emphasis_pattern, r'<em>\1</em>', '*This* is *it*!') 
'<em>This* is *it</em>!' 

如你所见,这个模式匹配了从第一个星号到最后一个星号的全部内容,其中包含另外两个星号!**这就是贪婪的意思:能匹配多少就匹配多少。 **
在这里,你想要的显然不是这种过度贪婪的行为。在你知道不应将某个特定的字符包含在内时,本章前面的解决方案(使用一个匹配任何非星号字符的字符集)很好。下面再来看另一个场景:如果使用’something’来表示突出呢?如何避免过度贪婪呢?
这实际上很容易,只需使用重复运算符的非贪婪版即可。对于所有的重复运算符,都可在后面加上问号来将其指定为非贪婪的。

>>> emphasis_pattern = r'\*\*(.+?)\*\*' 
>>> re.sub(emphasis_pattern, r'<em>\1</em>', '**This** is **it**!') 
'<em>This</em> is <em>it</em>!' 

这里使用的是运算符+?而不是+。这意味着与以前一样,这个模式将匹配一个或多个通配符,但匹配尽可能少的内容,因为它是非贪婪的。因此,这个模式只匹配到下一个’**’,即它末尾的内容。


6. 模板系统示例

代码清单10-11 一个模板系统

# templates.py 
import fileinput, re
# 与使用方括号括起的字段匹配
field_pat = re.compile(r'\[(.+?)\]')
# 我们将把变量收集到这里:
scope = {}
# 用于调用re.sub:
def replacement(match):
 code = match.group(1)
 try:
 # 如果字段为表达式,就返回其结果:
 return str(eval(code, scope))
 except SyntaxError:
 # 否则在当前作用域内执行该赋值语句
 # 并返回一个空字符串
 return ''
# 获取所有文本并合并成一个字符串:
#(还可采用其他办法来完成这项任务,详情请参见第11章)
lines = []
for line in fileinput.input():
 lines.append(line) 
text = ''.join(lines)
# 替换所有与字段模式匹配的内容
print(field_pat.sub(replacement, text))

简而言之,这个程序做了如下事情。

  • 定义一个用于匹配字段的模式。
  • 创建一个用作模板作用域的字典。
  • 定义一个替换函数,其功能如下。
    • 从match中获取与编组1匹配的内容,并将其存储到变量code中。
    • 将作用域字典作为命名空间,并尝试计算code,再将结果转换为字符串并返回它。如果成功,就说明这个字段是表达式,因此万事大吉;否则(即引发了SyntaxError异常),就进入下一步。
    • 在对表达式进行求值时使用的命名空间(作用域字典)中执行这个字段,并返回一个空字符串(因为赋值语句没有结果)。
    • 使用fileinput读取所有的行,将它们放在一个列表中,再将其合并成一个大型字符串。
    • 调用re.sub来使用替换函数来替换所有与模式field_pat匹配的字段,并将结果打印出来
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔚蓝慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值