[Python学习日记-45] Python 中模块的介绍与导入
简介
模块的概念与好处
模块的分类
模块导入和调用
自定义模块
模块的查找路径
简介
在前面的学习当中偶尔我们会看到 import ... 一个什么东西的,或者 from ... import ...,那时候并没有进行介绍,主要是那个时候还不会用到,其实我们做这个动作的时候就是引入一个模块,而 from ... import ... 是类似从一个工具包里面引入里面其中一个工具(模块),在后面的文章当中我们会介绍一下一些常用的模块,大概会有十来个偏向于基础的,更多的会在后面面向对象当中有更多的讲述,下面我们将好好的介绍一下到底什么是模块,以及应该如何导入模块吧。
模块的概念与好处
一、什么是模块?
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。为了编写可维护的代码,我们会把很多函数分组,并分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在 Python 中,一个 .py 文件就可以称之为一个模块(Module)。
二、使用模块有什么好处?
- 最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括 Python 内置的模块和来自第三方的模块。
- 使用模块还可以避免函数名和变量名冲突。每个模块有独立的命名空间,因此相同名字的函数和变量完全可以分别存在不同的模块中,所以我们自己在编写模块时,不必考虑名字会与其他模块冲突
模块的分类
模块根据不同的来源分为三种,分别是:
- 内置标准模块(又称标准库)执行 help('modules') 可以查看所有 Python 自带模块的列表,如下所示
help('modules') 的输出
Please wait a moment while I gather a list of all available modules...
test_sqlite3: testing with SQLite version 3.45.3
__future__ _testinternalcapi functools rlcompleter
__hello__ _testmultiphase gc runpy
__phello__ _testsinglephase genericpath sched
_abc _thread getopt secrets
_aix_support _threading_local getpass select
_ast _tkinter gettext selectors
_asyncio _tokenize glob shelve
_bisect _tracemalloc graphlib shlex
_blake2 _typing gzip shutil
_bz2 _uuid hashlib signal
_codecs _warnings heapq site
_codecs_cn _weakref hmac smtplib
_codecs_hk _weakrefset html sndhdr
_codecs_iso2022 _winapi http socket
_codecs_jp _wmi idlelib socketserver
_codecs_kr _xxinterpchannels imaplib sqlite3
_codecs_tw _xxsubinterpreters imghdr sre_compile
_collections _zoneinfo importlib sre_constants
_collections_abc abc inspect sre_parse
_compat_pickle aifc io ssl
_compression antigravity ipaddress stat
_contextvars argparse itertools statistics
_csv array json string
_ctypes ast keyword stringprep
_ctypes_test asyncio lib2to3 struct
_datetime atexit linecache subprocess
_decimal audioop locale sunau
_elementtree base64 logging symtable
_functools bdb lzma sys
_hashlib binascii mailbox sysconfig
_heapq bisect mailcap tabnanny
_imp builtins marshal tarfile
_io bz2 math telnetlib
_json cProfile mimetypes tempfile
_locale calendar mmap test
_lsprof cgi modulefinder textwrap
_lzma cgitb msilib this
_markupbase chunk msvcrt threading
_md5 cmath multiprocessing time
_msi cmd netrc timeit
_multibytecodec code nntplib tkinter
_multiprocessing codecs nt token
_opcode codeop ntpath tokenize
_operator collections nturl2path tomllib
_osx_support colorsys numbers trace
_overlapped compileall opcode traceback
_pickle concurrent operator tracemalloc
_py_abc configparser optparse tty
_pydatetime contextlib os turtle
_pydecimal contextvars pathlib turtledemo
_pyio copy pdb types
_pylong copyreg pickle typing
_queue crypt pickletools unicodedata
_random csv pip unittest
_sha1 ctypes pipes urllib
_sha2 curses pkgutil uu
_sha3 dataclasses platform uuid
_signal datetime plistlib venv
_sitebuiltins dbm poplib warnings
_socket decimal posixpath wave
_sqlite3 difflib pprint weakref
_sre dis profile webbrowser
_ssl doctest pstats winreg
_stat email pty winsound
_statistics encodings py_compile wsgiref
_string ensurepip pyclbr xdrlib
_strptime enum pydoc xml
_struct errno pydoc_data xmlrpc
_symtable faulthandler pyexpat xxsubtype
_testbuffer filecmp queue zipapp
_testcapi fileinput quopri zipfile
_testclinic fnmatch random zipimport
_testconsole fractions re zlib
_testimportmultiple ftplib reprlib zoneinfoEnter any module name to get more help. Or, type "modules spam" to search
for modules whose name or summary contain the string "spam".其实上面的输出就是 pycharm 中看到着这个目录里面的文件
该目录在 linux/mac 上会有差异,但是都大同小异,其实这个目录就在我们安装 Python 的根目录下
- 第三方开源模块,可通过 pip install 模块名联网安装
- 自定义模块,其实就是自己写的模块,后面会细说
模块导入和调用
在前面我们在看标准库的时候可以看到很多并不是只是一个 .py 文件,而是有很多个文件夹,这里我们要说说这个文件夹和 .py 文件到底是什么关系,在 Python 中,一个文件夹被称为一个包(模块),一个 .py 文件其实就是一个模块(子模块)。
理清这个关系之后我们一起来看看到底该如何导入模块到自己的代码当中吧
impot module_a # 导入模块(这个模块可以是一个文件夹或者只是一个 .py 文件)
from module import xx # 导入模块中的一个子模块或者函数
from module.xx.xx import xx # 导入多层结构的模块
from module.xx.xx import xx as rename # 导入后重命名
from module.xx.xx import * # 导入一个模块下的所有方法,不建议使用
modlue_a.xxx # 调用
注意:模块一旦被调用,即相当于执行了另外一个 .py 文件里的代码
在这里我们要解释一下为什么不建议使用 from module.xx.xx import *,我们先看看下面示例的代码
from os import *
print(name) # 不能使用 os.name
name = "Jove"
print(name)
代码输出如下:
从上面的代码的操作和输出结果可以看出,如果使用了 from module.xx.xx import * 这种调用方式是会少了上面提到的好处中的第二点避免函数名和变量名冲突,有一些模块会存在一些很常用的单词,例如上面代码中的 name 一样。
除了这一点,如果调用的两个模块存在相同名字的函数也会引起冲突,最终是后导入的模块会覆盖先导入的模块,这样有可能回到不到你开发的预期。
自定义模块
自定义模块是最简单的,我们创建一个 .py 文件,就可以把它称之为一个模块,就可以在另外一个程序里导入它,代码如下
建立一个自定义模块 my_module.py:
name = "Jove"
print("hello", name)
def say_hi(n):
print("hi,", n)
然后在同一目录下创建一个名为 use_module.py 的文件来调用该模块:
import my_module
my_module.say_hi("jok")
代码输出如下:
从输出里面可以看出,只要我调用了模块就会运行模块里面的代码了。好了,现在我们学会了如何调用模块了,随着我们代码规模的不断扩大,硬盘空间开始不够了,这个时候我们决定把一部分代码迁移出去,但是发现迁移过后的代码就无法调用 my_module.py 了,这是为什么呢?在上面的自定义模块代码中提议使用加粗字体强调了是在同一目录的环境下,而在同一目录环境下就可以顺利的调用,在其他目录下并不能调用,其实这个就跟 Python 的模块查找路径有关系。
模块的查找路径
在前面我们对为什么自己写的模块只能在当前路径下的程序里才能导入,换一个目录再导入自己的模块就抛出 ModuleNotFoundError 说找不到了,我们只要在命令行中打开 Python 来调用自己写的模块就能模拟另一个路径下调用了,错误如下图所示
关于这一个问题我们可以从内置模块 sys 哪里得到答案,我们先看看如下代码
import sys
print(sys.path)
代码输出如下(注意:不同的电脑可能输出的不太一样):
在命令行中(在 C:\Users\Administrator 下执行 Python):
[
'',
'D:\\2_Professional Software\\Python\\Python 3.12.4\\python312.zip',
'D:\\2_Professional Software\\Python\\Python 3.12.4\\DLLs',
'D:\\2_Professional Software\\Python\\Python 3.12.4\\Lib',
'D:\\2_Professional Software\\Python\\Python 3.12.4', # 标准库
'D:\\2_Professional Software\\Python\\Python 3.12.4\\Lib\\site-packages' # 第三方模块库
]
在 pycharm 中:
[
'G:\\joveProject',
'G:\\joveProject',
'D:\\2_Professional Software\\Python\\PyCharm\\PyCharm 2024.2\\plugins\\python-ce\\helpers\\pycharm_display',
'D:\\2_Professional Software\\Python\\Python 3.12.4\\python312.zip',
'D:\\2_Professional Software\\Python\\Python 3.12.4\\DLLs',
'D:\\2_Professional Software\\Python\\Python 3.12.4\\Lib',
'D:\\2_Professional Software\\Python\\Python 3.12.4',
'G:\\joveProject\\venv',
'G:\\joveProject\\venv\\Lib\\site-packages',
'D:\\2_Professional Software\\Python\\PyCharm\\PyCharm 2024.2\\plugins\\python-ce\\helpers\\pycharm_matplotlib_backend',
'D:\\2_Professional Software\\Python\\PyCharm\\PyCharm 2024.2\\plugins\\python-ce\\helpers\\pycharm_plotly_backend'
]
从输出可以看到,我这里显示了两种输出,分别是命令行和 pycharm 的,可以看到无论是数量和内容都会有不同。下面我们来说说这两个地方的输出不一样是什么原因:
- 首先,为什么 pycharm 输出的会是那么多项呢,这是因为 pycharm 为了让软件更好用它会加入一些自定义的库,所以才会多了那么多项
- 其次想必大家都注意到第一项的差别了,第一项其实就是当前目录的路径,pycharm 它会自己帮你把当前执行的目录的绝对路径放入到第一项中,而命令行则不会,只会显示'',而''就代表了当前目录 C:\Users\Administrator
接下来我们来说说模块超找路径的顺序,当你导入一个模块时,Python 解释器会按照上面列表顺序去依次到每个目录下去匹配你要导入的模块名,只要在一个目录下匹配到了该模块名,就立刻导入,不再继续往后找。
注意:列表第一个元素为空,即代表当前目录,所以你自己定义的模块在当前目录会被优先导入。
我们自己创建的模块若想在任何地方都能调用,那就得确保你的模块文件至少在模块路径的查找列表中。我们一般把自己写的模块放在一个带有“site-packages”字样的目录里,我们从网上下载安装的各种第三方的模块一般都放在这个目录。以 Windows 为例,下图为该目录的路径
在 pycharm 下:
在 Windows 下:
路径为:Python 安装的根目录\Lib\site-packages
导入后在命令行可以成功调用自己写的 my_module.py 模块了: