开箱即用-模块

目录

1.模块

1.1 模块即程序

1.2 关于模块

1.2.1 模块中添加测试代码

1.2.2 让模块可用

1.2.3 包

2. 模块探索

2.1 模块包含什么

2.1.1 dir命令

2.1.2 变量__all__

2.1.3 使用help获取帮助

2.1.4 使用源代码

3.标准库

3.1 time

3.2 random

3.3 正则表达式re

3.3.1 正则表达式

3.3.2 模块re的内容


1.模块

import 将函数从外部模块导入到程序中,那我们应该如何编写自己的模块呢?

1.1 模块即程序

任何的Python程序都可以作为模块导入。假如我们有个hello.py文件,代码如下:

print('Hello,World!')

test.py导入hello模块:

import Hello

运行test.py代码,执行结果如下:

Hello,World!

当我们导入模块时,发现会在目录中除源代码文件外,还创建了一个名为__pycache__的子目录。

该目录包含处理后的文件,Python能够更高效地处理它们。以后再导入这个模块时,如果.py文件未发生变化,Python将导入处理后的文件,否则将重新生成处理后的文件。删除目录__pycache__不会有任何害处,因为必要时会重新创建它。

模块在导入时,会执行其中的代码。但如果再次导入它,代码就不会再执行。这是因为模块并不是用来执行操作的,而是用于定义变量、函数和类等。鉴于定义只需要做一次,因此导入模块多次和导入一次的效果是相同的。

如果一定要重新加载模块,可使用模块importlib中的函数reload,它接受要重新加载的模块作为参数,并返回重新加载的模块。如果在程序运行中修改了模块,并希望这种修改反映到程序中,reload函数时非常有用的。看如下的实例代码:

import hello
import hello
import hello

print('导入了3次仅执行1次')

from importlib import reload as re

hi = re(hello)

输出结果为:
Hello,World!
导入了3次仅执行1次
Hello,World!

1.2 关于模块

模块拥有自己的作用域,这意味着在模块中定义的类和函数以及对其进行赋值的变量都将成为模块的属性。创建模块主要是为了重用代码,通过将代码放在模块中,就可以在多个程序中使用它们。因此,要让代码是可重用的,务必将其模块化。

1.2.1 模块中添加测试代码

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

def hello():
    print('Hello,World!')

#测试函数
hello()

这种测试方式存在一个问题就是,如果在另外一个程序中将其作为模块导入,测试代码会被执行。要避免这种行为,关键是检查模块是作为程序运行还是被导入另一个程序。为此,需要使用变量__name__。

在主程序中,变量__name__的值是 __main__,而在导入的模块中,这个变量被设置为该模块的名称。因此,要让模块中测试代码的行为更加合理,可以将其放在一条if语句中。

def hello():
    print('Hello,World!')

if __name__ == '__main__':
    hello()

1.2.2 让模块可用

让模块可用,需要将模块放在正确的位置。因此,我们只需要找出Python解释器到哪里去查找模块,再将文件放在这个地方即可。可以在模块sys的变量path中找到Python的搜索路径。

import sys, pprint

pprint.pprint(sys.path)

输出结果如下:

['C:\\woker\\mycode\\python-martin\\com\\basic\\lec9',
 'C:\\woker\\mycode\\python-martin',
 'E:\\Anaconda3\\python37.zip',
 'E:\\Anaconda3\\DLLs',
 'E:\\Anaconda3\\lib',
 'E:\\Anaconda3',
 'E:\\Anaconda3\\lib\\site-packages',
 'E:\\Anaconda3\\lib\\site-packages\\win32',
 'E:\\Anaconda3\\lib\\site-packages\\win32\\lib',
 'E:\\Anaconda3\\lib\\site-packages\\Pythonwin',
 'C:\\woker\\PyCharm2018.1.1\\helpers\\pycharm_matplotlib_backend']

如果要打印的数据结构太大,一行容纳不下,可使用模块pprint中的函数pprint。该函数是一个卓越的打印函数,能够更加妥善地打印输出。如果要让解释器能够找到模块,可将其放在其中任何一个位置上,但目录site-packages是最佳的选择,因为它就是用来放置模块的。但很多时候,Python管理器是管理员安装的,我们没有管理员权限,因此无法将模块保存到上述目录中。与此同时,我们可能也不希望Python解释器的目录中充斥着我们自己编写的代码。此时,我们可以修改操作系统的环境变量PYTHONPATH,将模块所在的目录包含在环境变量PYTHONPATH中。

1.2.3 包

为组织模块,可将其编组为包。包其实就是另一种模块,它可包含其他模块。模块存储在扩展名.py的文件中,而包则是一个目录。要被Python视为包,目录必须包含文件__init__.py。如果像普通模块一样导入包,文件__init__.py的内容就将是包的内容。要将模块加入包中,只需将模块文件放在包目录中即可。一种简单的包布局如下:

完成上面的准备工作后,下面的语句将是合法的:

import drawing             #(1)导入drawing包

import drawing.colors   #(2)导入drawing包中的模块colors

from drawing import shapes     #(3)导入模块shapes

执行上面第1条语句后,即可使用目录drawing中文件__init__.py的内容,但是不能使用模块shapes和colors的内容。执行第2条语句后,便可使用模块colors,但只能通过全限定名drawing.colors来使用。执行第3条语句之后,即可使用简化名(shapes)来使用模块shapes。

2. 模块探索

要探索模块,最直接的方式是使用Python解释器进行研究,为此,首先需要将模块导入。假设我们听说有一个名为copy的标准模块,执行import copy,没有引发异常,说明确实有这样的模块。但这个模块是做什么用的呢?包含些什么呢?

2.1 模块包含什么

2.1.1 dir命令

要查明模块包含哪些东西,可使用函数dir,它列出了对象的所有属性:

import copy

print(dir(copy))

输出结果如下:

['Error', '__all__', '__builtins__', '__cached__', '__doc__', '__file__',
'__loader__', '__name__', '__package__', '__spec__', '_copy_dispatch', 
'_copy_immutable', '_deepcopy_atomic', '_deepcopy_dict', '_deepcopy_dispatch', '_deepcopy_list', '_deepcopy_method', '_deepcopy_tuple', '_keep_alive', 
'_reconstruct', 'copy', 'deepcopy', 'dispatch_table', 'error']

在这些名称中,有几个是以下划线打头。根据约定,这意味着它们并非供外部使用。我们可以使用一个简单的列表推到将这些名称过滤掉。

import copy

li = [n for n in dir(copy) if not n.startswith('_')]
print(li)
输出结果如下:
['Error', 'copy', 'deepcopy', 'dispatch_table', 'error']

2.1.2 变量__all__

在dir(copy)返回的列表清单中,包含名称__all__。这个变量包含一个列表,我们看一下该列表包含的内容:

import copy
print(copy.__all__)
输出结果如下:
['Error', 'copy', 'deepcopy']

__all__变量旨在定义模块的共有接口,具体来说它告诉解释器从这个模块导入所有的名称意味着什么。

import copy
print(copy.__all__)
输出结果如下:
['Error', 'copy', 'deepcopy']

因此,我们使用如下代码:

from copy import *

将只能得到变量__all__中列出的3个函数。要导入PyStringMap,必须显式地导入copy并使用copy.PyStringMap或者使用from copy import PyStringMap。编写函数时,像这样设置__all__也是很有用的,因为模块可能包含大量其他程序不需要的变量、函数和类,比较周全的做法是将它们过滤掉。如果不设置__all__,则会在以import *方式导入时,导入所有不以下划线打头的全局名称。

2.1.3 使用help获取帮助

Python中可以使用help函数来获取其他函数的信息。例如我们想获得copy模块中copy函数的相关信息,可以执行如下代码:

import copy

help(copy.copy)
输出结果如下:
Help on function copy in module copy:

copy(x)
    Shallow copy operation on arbitrary Python objects.
    
    See the module's __doc__ string for more info.

我们也可以查看函数的文档信息,来查看函数的详细描述信息:

import copy

print(copy.copy.__doc__)

输出结果如下:
Shallow copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.

当然,我们也可以直接查看“Python库参考手册”,该手册描述了标准库中的所有模块。

2.1.4 使用源代码

要学习Python,阅读源代码是除手动编写代码以外的最佳方式。但源代码具体存放在哪里呢?我们可以查看模块的特性__file__来查找源文件的路径:

import copy

print(copy.__file__)


输出结果为:
E:\Anaconda3\lib\copy.py

找到源文件以后,我们就可以在代码编辑器中打开文件copy.py来查看源代码。这里需要注意的是,在关闭文件时,千万不要保存我们对其所做的修改。

3.标准库

本节会介绍一些常用的系统模块。

3.1 time

模块time包含用于获取当前时间、操作时间和日期、从字符串中读取日期、将日期格式化为字符串中的函数。日期可以表示为实数也可以表示为包含9个整数的元组。如果表示为9个整数的,则每个整数表示的含义如下:

秒的取值范围是0~61,这考虑到了闰一秒和闰两秒的情况。夏令时数字是一个布尔值(True:1,False:0),但是如果使用-1,那么mktime(将时间元组转换为时间戳的函数)可能得到正确的值。元组(2008, 1, 21, 12, 2, 56, 0, 21, 0)表示2008年1月21日12时2分56秒,这一天是星期一, 2008年的第21天(不考虑夏令时)

下面的表格描述了模块time中的一些最重要的函数:

3.2 random

 模块random包含生成伪随机数的函数,有助于编写模拟程序生成随机输出的程序。请注意,虽然这些函数生成的数字好像是完全随机的,但它们背后的系统是可预测的。如果要求真正的随机(如用于加密或实现与安全相关的功能),应该考虑使用,模块os中的函数urandom。模块random中的SystemRandom类基于的功能与urandom类似,可提供接近于真正随机的数据。

3.3 正则表达式re

模块re提供了对正则表达式的支持。

3.3.1 正则表达式

正则表达式是可匹配文本片段的模式,正则表达式主要有如下的匹配模式:

  • 通配符:正则表达式可与多个字符串匹配,我们可以使用特殊字符来创建这种正则表达式。句点与除换行符以外的其他字符都是匹配的,但是只能与一个字符匹配,而不与0或两个字符匹配。例如正则表达式“.ython”与字符串“python”、“+ython”、“ ython”等字符串都是匹配的,但是和“cpython”、“ython”是不匹配的。
  • 对特殊字符进行转义:普通字符只与自身匹配,但是如果有特殊字符的话就完全不同了。例如,假设要匹配字符串“python.org”,我们可以直接使用“python.org”,但是它也与“pythonzorg”匹配,这不是我们想要的结果。要让特殊字符的行为与普通字符一样,可以对其进行转义,在它的前面加上一个反斜杠。因此,在这个实例中,可以使用模式‘python\\.org’,它只与“python.org”进行匹配。为表示模块re要求的单个反斜杠,需要在字符串中书写两个反斜杠,让解释器对其进行转义。这里包含两层转义:解释器执行的转义和模块re执行的转义。
  • 字符集:匹配任何的字符都是很有用的,但有时我们需要更加细致地控制。为此,可以用方括号将一个子串括起,创建一个所谓的字符集。这样的字符集与其包含的字符都匹配,例如“[pj]ython”与“python”和“jython”都匹配。我们还可以指定范围,[a-z]/[A-Z]/[0-9]等,需要注意的是字符集只能匹配一个字符。要指定排除字符集,可在开头添加一个^字符,例如[^abc]与除a、b、c外的其他任何字符都匹配。
  • 二选一和子模式:字符集能够帮助我们以不同的方式处理每个字符,但如果仅仅只想匹配字符串“python”和“perl”时,使用字符集或通配符就无法指定这样的模式,而必须使用表示二选一的特殊字符:管道字符(|),模式为“python|perl”。有时候我们不想讲二选一运算符用于整个模式,而只想将其用于模式的一部分。为此,可将这部分(子模式)放到圆括号内。前面可以重写为“p(ython|erl)”。
  • 可选模式和重复模式:通过在子模式后面加上问号,可将其指定为可选的,即可包含可不包含。模式(http://)?(www\.)?python\.org与http://www.python.org、http://python.org、python.org、www.python.org 这些字符串都是匹配的
  • 其他子模式:(pattern)*:pattern可重复0、1或多次;(pattern)+:pattern可重复1到多次;(pattern){m,n}:模式可重复m~n次
  • 字符串的开头和结尾:我们有时想要匹配字符串的开头和结尾,例如想确定字符串的开头是否与模式“ht+p”匹配,可以使用脱字符(“^”)来实现,“^ht+p”与“http://python.org”和“htttttp://python.org”匹配;同样要指定字符串的末尾,可使用美元($)

3.3.2 模块re的内容

模块re中包含多个使用正则表达式的函数:

函数re.compile将用字符串表示的正则表达式转换为模式对象,以提高匹配效率。调用search、match等函数时,如果提供的是用字符串表示的正则表达式,都必须在内部将它们转换为模式对象。通过使用函数compile对正则表达式进行转换后,每次使用它时都无需再进行转换。模式对象也有搜索匹配方法,因此re.search(pat,string)等价于pat.search(string)。

函数re.search在给定字符串中查找第一个与指定正则表达式匹配的子串。如果找到这样的子串,将返回MatchObject(结果为真),否则返回None(结果为假)。函数re.split根据与模式匹配的子串来分隔字符串,这类似于字符串方法split,但使用正则表达式来指定分隔符,而不是指定固定的分隔符。实例代码如下:

import re

pat = '(http://)?(www\.)?python\.org'
string = 'http://www.python.org'
if re.search(pat, string):
    print('find it')

if re.match('h', string):
    print('开头匹配成功')
if not re.match('y', string):
    print('开头匹配失败')

# 字符串分隔
some_text = 'alpha,beta,,,,gama delta'
print(re.split('[, ]+', some_text))
# maxsplit指定最多的分割次数
print(re.split('[, ]+', some_text, maxsplit=2))
print(re.split('[, ]+', some_text, maxsplit=1))

输出结果如下:
find it
开头匹配成功
开头匹配失败
['alpha', 'beta', 'gama', 'delta']
['alpha', 'beta', 'gama delta']
['alpha', 'beta,,,,gama delta']

函数findall返回一个列表,其中包含所有与给定模式匹配的子串。例如,要找出字符串包含的所有的单词,可像下面这样做:

import re

pat = '[a-zA-Z]+'
string = '"Hello,are you sure?" he said'
print(re.findall(pat, string))
输出代码如下:
['Hello', 'are', 'you', 'sure', 'he', 'said']

函数escape是一个工具函数,用于对字符串中所有可能被视为正则表达式运算符的字符进行转义。该函数的使用情况有:字符串很长,其中包含大量的特殊字符,而我们又不想输入大量的反斜杠或用户输入一个字符串,直接用于正则表达式。代码如下:

import re

print(re.escape('www.baidu.com'))
输出结果如下:
www\.baidu\.com

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值