Python模块与包

一、模块

1. 什么是模块

模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能。这样可以让程序的组织结构更清晰,方便管理。

2. 模块的分类

  • 内置的模块,Python解释器中已经存在的,比如time,datetime,random,sys, os,re正则模块等
  • 第三方,别人写好的传到网上的
  • 自定义的,自己写的模块

3. 模块的表现形式

  • 使用python编写的代码(.py文件), 一个py文件就是一个模块。
  • 已被编译为共享库或DLL的C或C++扩展
  • 把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
  • 使用C编写并链接到python解释器的内置模块

4. 模块的查找顺序

    先从内存中找 ==》再从内置中找 ==》 最后从sys.path路径中找

  • 在第一次导入某个模块时,会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用。Python解释器在启动时会自动加载一些模块到内存总,可以使用sys.modules查看。
  • 内存中如果没有,解释器则会查找同名的内建模块。(所以特别注意我们自定义的模块不要与系统内置模块重名)
  • 如果内置中也没有,就从sys.path给出的目录列表中依次寻找。
Pycharm会自动将.py文件的父目录和根目录加入到sys.path

查看环境变量路径:
 import sys
 print(sys.path)

当执行文件时找不到模块名时,可以手动将模块名所在路径加入到sys.path中 
sys.path.append(r'绝对路径') #windows下的路径不加r开头,会语法错误

5. 使用模块

我们可以随意对模块文件命名,但是文件扩展名必须是 .py

模块中的变量:模块可以包含函数,也可以包含各种类型的变量(数组、字典、对象等)

内置函数dir() 可以列出模块中的所有函数名(或变量名)

from .page import CommonPageNumberPagination

x = dir(CommonPageNumberPagination)
print(x)
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'display_page_controls', 'django_paginator_class', 'get_html_context', 'get_next_link', 'get_page_number', 'get_page_size', 'get_paginated_response', 'get_paginated_response_schema', 'get_previous_link', 'get_results', 'get_schema_fields', 'get_schema_operation_parameters', 'invalid_page_message', 'last_page_strings', 'max_page_size', 'page_query_description', 'page_query_param', 'page_size', 'page_size_query_description', 'page_size_query_param', 'paginate_queryset', 'template', 'to_html']

① import句式

# 在学习模块的时候,首先分清楚谁是执行文件,谁是导入文件
import md  # 导入的模块不要加后缀

'''
模块被导入:
1. 模块会执行,但是导入多次,只会执行一次
        
模块首次被导入的时候发生了什么事?
1. 运行执行文件,产生一个执行文件的全局名称空间
2. 运行被导入文件
3. 导入文件中的名字放入到导入文件的名称空间
4. 在执行文件的名称空间中有一个md的变量指向导入文件的名称空间
    
import句式注意事项:
在自己的名称空间中如果有与导入文件相同的名字,在调用的时候,指名道姓的调用,不会产生冲突的
'''

② from...imoport...句式

from md import test1
'''        
from...import句式首次被导入:
1. 产生一个执行文件的名称空间
2. 会运行导入文件
3. 产生一个导入的名称空间
4. 在执行文件中产生一个test1,执行导入文件的test1,导入文件中的其他没有被导入的不能在执行文件中使用,如果想使用,继续导入过来就可以使用        
'''

连续导入:from md import test1, test2, test3

import和from...imoport...的区别:
唯一的区别就是使用from...import...是将md中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀:md.
#from...import...的方式有好处也有坏处
好处:使用起来方便了
坏处:容易与当前执行文件中的名字冲突

③ as起别名

可以给模块起别名,也可以给模块里的某一个名字起别名

# 为 mymodule 创建别名 mx:
import mymodule as mx

a = mx.person1["age"]
print(a)  # 18

④ from...import * 导入所有变量

(注:这种导入方法只能导入公有的属性、方法和类,不能导入私有的或者保护型的属性方法类。)

from md import *  # 把md所有的不是以下划线(_)开头的名字都导入到当前位置

#大部分情况下我们的python程序不应该使用这种导入方式,因为不知道*导入了哪些名字,很有可能会覆盖掉我们之前已经定义的名字。而且可读性差。

可以在md.py 中使用__all__来控制*,新增一行:

__all__=['test1',test2']   # 这样在另外一个文件中用from md import * 就这能导入列表中规定的两个名字

⑤ 判断文件类型:模块与脚本

  一个Python文件可以有两种用途:

  • 模块:文件中存放一堆功能,用来被导入
  • 脚本:一个文件就是整个程序,用来被执行

  Python为我们内置了全局变量__name__,用来判断一个py文件是执行文件还是当成模块导入的文件。

  • 当文件被当做模块导入时:__name__等于模块名
  • 当文件被当做脚本执行时,__name__等于 '__main__'
# 判断一个py文件是执行文件还是当成模块导入的文件
# print(__name__, type(__name__))   # '__main__'

# 作用:用来控制.py文件在不同的应用场景下执行不同的逻辑 if __name__ == '__main__': test()

⑥ 不要出现循环导入问题

模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码。

在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享的数据,可以将共享的数据集中存放到某一个地方。

6. 绝对导入和相对导入

① 绝对导入

  • 绝对导入都是从sys.path环境变量中查找的。
  • 导入模块的时候,始终以执行文件为准;
  • 找不到模块名,将模块的路径加入到sys.path中,或者使用from…import…
  • 绝对导入一般格式如下:
import md.test1
from md import test1 

② 相对导入

  • 相对导入不在sys.path中查找,只跟当前文件位置有关,主要使用句点符导入.
  • . 代表的是当前目录,.. 代表的是上一级目录 
  • 相对导入不能在执行文件中使用。文件中使用了相对导入的路径,就不能作为执行文件(在执行文件中,不能出现相对导入)
  • 执行文件只能有一个,其他都是被导入文件
  • django所有位置都可以用相对导入,除了settings.py文件;
  • Django项目名中导入自己写的模块,都用最短路径去导入。因为每导入一层,它会把里面所有的代码都去执行,里面可能会引发循环导入问题。
  • 有时候导入,pycharm会飘红提示,但并不是导入有问题。解决办法:鼠标放在包名点右键,Mark Directory as--Sources Root,将它加入资源路径。加入后如果还是飘红,关掉重新打开py文件。
  • 相对导入的一般格式如下:
from . import m1 
from .. import m1
from ..md import m1
from .md import m1

 ③ 总结三句话

  1. 导入模块的路径,需要从环境变量下开始导,要是不在环境变量,就自己加上。
  2. 执行脚本所在路径,会自动加入到环境变量。(点右键一运行就自动加入了)
  3. 可以使用相对导入和绝对导入。但使用相对导入的py文件,不能作为脚本去运行。

二、包

1. 什么是包

  • 包是一种通过使用 '.模块名' 来组织python模块名称空间的方式。
  • 具体来说,包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来。
  • 创建包的目的不是为了运行,而是被导入使用。(包内的模块是用来被导入的,而不是被执行的)
  • 也可以说包的本质就是一个文件夹,文件夹的功能就是将文件组织起来,随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性。

2. 包里面的__init__.py文件有什么作用?

__init__.py该文件的作用就是相当于把自身整个文件夹当作一个包来管理,每当有外部import的时候,就会自动执行里面的函数。该文件就是一个正常的python代码文件。因此可以将初始化代码放入该文件中。它可以简化导入操作,当外部想用到包里子模块里的变量(方法或属性),可以在__init__.py中导入这些变量,外部用的时候,就不用导得那么深,只需要直接导这个变量就可以了。

3. 包的使用

导入包就是在导入包下的__init__.py

关于包相关的导入语句也分为 import 和 from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:

凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如aa.bb.cc,但都必须遵循这个原则。

但对于导入后,在使用时就没有这种限制了,点的左边可以是包、模块、函数、类(它们都可以用点的方式调用自己的属性)。

使用语句中的点代表的是访问属性
如 m.n.x ----> 向m要n,向n要x

导入语句中的点代表的是路径分隔符
如 import a.b.c --> a/b/c,文件夹下a下有子文件夹b,文件夹b下有子文件或文件夹c

所以导入语句中点的左边必须是一个包

包内部的导入应该使用相对导入,相对导入也只能在包内部使用,而且用点的方式取上一级不能出包。

import 导入文件时,产生名称空间中的名字来源于文件。import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件。包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间。

posted @ 2022-10-31 10:20  Tutu007  阅读(87)  评论(0编辑  收藏  举报