Python模块

1.模块

使用import将函数从外部模块导入到程序中。

>>>import math
>>>math.sin(0)
0.0

任何Python程序都可以作为模块导入。
假如你创建了一个模块,并将其存储在文件W.py中,这个文件的名称(不包括扩展名.py)将成为这个模块的名称

#W.py
print('你好')
#假设文件存储在目录C:/python中(UNIX/macOS为~/python)
>>>import sys
>>>sys.path.append('C:/thon')
#这告诉解释器,除了通常要查找的位置外,还应到目录C:/thon中查找。这样做就可以导入这个模块
>>>import W
你好
#如你所见,倒入这个模块时,执行了其中的代码。但如果再次倒入它,什么都不会发生。
>>>import W

模块不是用来执行操作的的,而是用来定义变量,函数,类等等的。因此导入模块多次和导入一次的结果相同。


在大多数情况下,只导入一次时重要的优化,且在两个模块批次导入对方时格外有用。
在很多情况下,你可能需要编写需要彼此访问对方的函数和类才能正确的发挥作用。例如:你创建了两个模块——clientdb、billing,分别包含客户数据库和记账系统的代码。客户数据库可能需要调用记账系统(如每月定时向客户发送月度账单),而记账系统也需要访问客户数据库才能实现正确记账。
在这里,如果每个模块都可导入多次,那就会引发程序错误。因为二次导入时什么都不会发生,这种有模块clientdb导入billing,模块billing导入clientdb形成的循环将被打破。
若要重新加载模块,可以使用模块importlib中的函数reload,它接受一个参数(需要重新加载的模块),并返回重新加载的模块。若在程序运行中修改了模块,并希望这种改变反映在程序中,这将是一种很好用的方法。
重新加载模块W

>>>import importlib
>>>W=import.reload(W)
你好

2.定义模块

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

1).在模块中定义函数

假设你创建一个模块W2

#W2.py
def W():
	print('你好!')
>>>import W2		#像这样导入模块
>>>W2.W()
你好!

在模块的全局作用域内定义的名称都可像上面那样访问。那为什么不在主程序中定义一切呢?
主要是为了重用代码。通过将代码放入模块中,就意味着可以在多个程序中运用它。

2).在模块中添加测试代码

在有些情况下,需要添加一些测试代码来检查情况是否符合预期。
模块W3

#W3.py
def W():
	print('你好!')
W()		#测试
#将模块W3看作普通程序运行,会发现它运行正常。然而当它作为模块导入时,也将执行测试代码。
>>>import W3
你好!
>>>W3.W()
你好!
#这并不是你想要的结果。为避免这种行为,可以使用变量__name__。关键在于检查模块是作为程序运行还是作为模块导入程序
>>>__name__
'__main__'
>>>W3.__name__
'W3'

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

#W4
def W():
	print('你好!')
def test():
	W()
if __name__=='__main__':test()
#如果将这个代码作为程序运行,将执行函数W;若导入它,其行为和普通模块相同。
>>>import W4
>>>W4.W()
你好!
#将测试代码放在了test中。可以将其放在if语句中,但通常情况下都是将其放在一个独立的测试函数中,可在程序中导入模块时对其进行测试。
>>>W4.test()
你好!

3.让模块可用

sys.path包含一个目录列表,解释器将在这些目录中查找模块。
理想情况下,sys.path一开始就包含正确的目录(你模块所在的目录)。为此有两种办法:将模块放在正确的位置;告诉解释器到哪里去查找。

1).将模块放在正确的位置

将模块放在正确的位置很容易,只需找出Python解释器到哪里去查找模块,再将文件放在这个地方即可。
可在模块sys的变量path中找到目录列表(即搜索路径)

>>>import sys, pprint
>>>pprint.pprint(sys.path)
['',
 '/Users/LM/Documents',
 '/Library/Frameworks/Python.framework/Versions/3.10/lib/python310.zip',
 '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10',
 '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload',
 '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages']

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


每个字符串都表示一个位置,若要让解释器能够找到模块,可将其放在其中任何一个位置中。但是目录site-packages是最佳的选择。

>>>import another_W
>>>another_W.W()
你好!

只要模块位于类似于site-packages这样的地方,所有的程序都可以导入它。

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

将模块放在正确的位置可能并不是一个合适的解决方案。原因有很多。
1.不希望Python解释器的目录充斥着你编写的代码。
2.没有必要的权限,无法将文件保存到Python解释器的目录中。
3.想将模块放在其他地方。
4.最重要的是,若将模块放在其他地方,就必须告诉解释器到哪里查找。方法之一是直接修改sys.path,但这种做法并不常见。标准做法是将模块所在的目录包含在环境变量PYTHONPATH中。


                             环境变量

环境变量并不是Python解释器的一部分,是操作系统的一部分。类似于Python变量,但是在Python解释器外面设置的。若你使用的是bash shell(在大多数类UNIX系统、macOS和较新的Windows版本中都有。)可使用export PYTHONPATH将~/python添加在变量PYTHONPATH末尾。
若要对所有启动的shell都执行这个命令,可将其添加到主目录中的.bashrc文件中。


还可以使用路径配置文件。这些文件的扩展名为.pth,位于一些特殊的目录中,包含要添加到sys.path中的目录。

4.包

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

import constants
print(constants.PI)

要将模块加入包中,只需将模块文件放在包目录中即可。还可以在包中嵌套其他包。例如:要创建一个名为drawing的包,其中包含模块shapes和colors,需要创建文件和目录。


                          一种简单的包布局

完成这些准备工作后,下面的语句就是合法的:

import drawing                #(1)导入drawing包
import drawing.colors         #(2)导入drawing包中的模块colors
from drawing import shapes    #(3)导入模块shapes

执行第一条语句后,便可使用目录drawing中文件__init__.py的内容,但不能使用模块shapes和colors的内容。执行第二条语句后,便可使用模块colors,但只能通过全限定名drawing.colors来使用。执行第三条语句后,便可使用简化名(即shapes)来使用模块shapes.
也可以只使用第二条语句或第三条语句。

5.模块包含什么

要探索模块,最直接的方式是使用Python解释器进行研究。为此,首先需要将模块导入。假设你听说过有一个名为copy的标准模块。

>>>import copy		#没有引发异常,说明有这样的模块。

但这个模块有什么用,包含些什么呢?

1).使用dir

要查明模块包含哪些方法,可使用函数dir,它列出对象的所有属性。

>>>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']
>>>[n for n in dir(copy) if not n.startswith('_')]
#过滤了以下划线打头的名称,要比完整清单易懂些。
['Error', 'copy', 'deepcopy', 'dispatch_table', 'error']
2).变量__all__

这个变量包含了一个列表

>>>copy.__all__
['Error', 'copy', 'deepcopy']

那么__all__列表是如何来的呢?
它是在模块copy中像下面这样设置的(这些代码直接从copy.py复制而来的):

__all__=['Error', 'copy', 'deepcopy']

为什么要提供它呢?
旨在定义模块的公有接口。它告诉解释器从这个模块导入所有的名称意味着什么。因此,如果使用如下代码:

from copy import *

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

6.使用help

使用help获取有关函数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.

上述指出,函数copy只接受一个参数x,且执行的是浅复制。在帮助信息中,还提到了模块的__doc__字符串。文档字符串是在函数开头编写的字符串,用于对函数进行说明,而函数的属性__doc__可能包含这个字符串。

>>>print(copy.copy.__doc__)
Shallow copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.

相比于直接查看文档字符串,使用help的优点是可以获取更多的信息。如函数的特征标(它所接受的参数)。

7.文档

文档是有关模块信息的自然来源。
例如:你可能想知道range的参数是什么?这时,与其在Python图书或标准Python文档查找对range的描述,不如直接检查这个函数。

>>>print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

这样就获得了函数range的准确描述。但并不是每一个模块、函数都有详尽的文档字符串,就学习Python编程而言,最有用的文档是“Pytnon库参考手册”

8.使用源代码

想要真正了解Python语言,就需要学会阅读源代码。
可以像解释器那样通过sys.path来查找,也可以查看模块的特性__file__。

print(copy.__file__)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/copy.py

你可以在代码编辑器上(如IDIE)中打开文件copy.py,并开始研究其工作原理。如果列出的文件以.pyc结尾,可以打开以.py结尾的文件。


                                警告 

在文本编辑器中打开标准库文件时,存在不小心修改它的风险。这可能会破坏文件,所以在关闭文件时,不要保存你做出的修改。


有些模块的源代码可能完全无法读懂,它们可能是解释器的组成部分(如模块sys),也有可能是使用C语言编写的。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值