Python中的模块、包、库的区别

此为学习笔记的整理与记录,方便个人查阅、回顾、复习

一、模块

(一)什么是模块?

  就是扩展名为.py的文件,里面定义了一些函数和变量。
  通过使用模块,不仅可以有效地避免命名空间的冲突,还可以将一个较大的程序分为多个文件,提升代码的可维护性和可重用性。

(二)模块的导入

  【点击查看模块导入示例】

  1. import 模块名 [as 别名1], 模块名2 [as 别名2],…
  2. from 模块名 import 成员名1 [as 别名1],成员名2 [as 别名2],…

  其中,用 [] 括起来的部分,可以使用,也可以省略。

  注意!尽量不要使用from demo import *的方式进行导入,否则很容易出现名称重复的情况。
  例如,如果有两个模块里面有相同的成员,或者你自己定义了某一个对象和demo module里面的成员(例如函数)重名了,运行的时候会出问题。因为此时我们无法确认,我们所指的是自己定义的对象还是demo module里面的成员。

(三)if __name __ == ’ __main __':

  在完成一个模块的编写之前,我们一般会对模块中的功能进行测试,看看各项功能是否正常运行。对于这些测试的代码,我们希望只在直接运行这个py文件的时候执行,在用其他的程序import这个模块的时候不要执行。这个时候就要借助Python内置的__name__变量

  1. 当一个py文件被直接运行的时候,__ name__变量的值为__main__。
  2. 当一个py文件被import到其他程序的时候,这个py文件里面的__name__变量的值为这个py文件的名字。

  利用__name__变量的这个特点,结合一个if语句,让我们import某一个模块的时候只用到它提供的功能,而不要运行里面用来测试的代码

(四)模块的路径

  对于用import语句导入的模块,Python会按照下面的路径列表顺序地查找我们需要的模块:

  1. 当前的工作目录
  2. PYTHONPATH(环境变量)中的每一个目录
  3. Python 默认的安装目录

  如果我们写的模块没有放在上述三类路径,那么在导入时,Python解释器提示找不到这个模块 ModuleNotFoundError: No module named ‘模块名’

▲解决方法

向sys.path变量中临时添加模块文件所在的完整路径

import sys
sys.path.append(r'D:\xxx\yyy')

将模块移动到sys.path 变量中已包含的路径中
修改path 系统的环境变量

  其中,环境变量又分为用户变量(仅对当前用户生效)和系统变量(对系统里所有用户均生效)。Python在使用path变量时,会先按照系统path变量找,然后按照用户path变量的路径找。通常情况下,设置用户path变量即可

二、 包

(一)什么是包?

  在模块之上的概念,为了方便管理而将文件进行打包。包目录下第一个文件必须是__init__.py,否则就是普通股的文件夹目录。然后就是一些模块文件和子目录,假如子目录中也有__init__.py,那么它就是这个包的子包了。如下图目录结构所示:

package
  |- __init__.py
  |- subpackage1
      |- __init__.py
      |- a.py
  |- subpackage2
      |- __init__.py
      |- b.py
  |- c.py

Python包具有3个性质

  1. 它实质上是一个文件夹
  2. 该文件夹里面一定有__ init__.py模块,其他的模块可以有也可以没有
  3. 它的本质依然是模块,因此一个包里面还可以装其他的包

(二)包的导入

  导入包的方法和导入模块比较类似,只不过由于层级比一般模块多了一级,所以多了一条导入形式。

  1. import 包名[.模块名 [as 别名]]
  2. from 包名 import 模块名 [as 别名]
  3. from 包名.模块名 import 成员名 [as 别名]

  其中,用 [] 括起来的部分,可以使用,也可以省略。我们在导入包的时候,实际上是导入了它的__init__.py文件

三、 库

  具有相关功能模块、包的集合。这也是python的一大特色之一,即具有强大的标准库、第三方库以及自定义模块。

  • 标准库:下载安装的python里那些自带的模块。
  • 第三方库:就是由第三方机构发布的,具有特定功能的模块。
  • 自定义模块:自己编写使用的模块。

一、模块导入示例

|-main.py
|-arithmetic
   |-add.py
   |-sub.py
   |-mul.py
   |-mul.py

  文件目录结构如上。其中,main.py是程序入口,用不同的方式来import四则运算的各个子模块,arithmetic模块实现四则运算。
  每个文件的代码如下:

#
# @file main.py
#

import arithmetic.add
import arithmetic.sub as sub

from arithmetic.mul import mul
from arithmetic import dev

def letscook(x, y, oper):
    r = 0
    if oper == "+":
        r = arithmetic.add.add(x, y)
    elif oper == "-":
        r = sub.sub(x, y)
    elif oper == "*":
        r = mul(x, y)
    else:
        r = dev.dev(x, y)

    print("{} {} {} = {}".format(x, oper, y, r))
-----------------------------------------------------------------
#
# @file add.py
#

def add(a, b):
    return a + b
-----------------------------------------------------------------
#
# @file sub.py
#

def sub(a, b):
    return a - b
-----------------------------------------------------------------
#
# @file mul.py
# 

def mul(a, b):
    return a * b
-----------------------------------------------------------------
#
# @file dev.py
#

def dev(a, b):
    return a / b

二、__init __.py模块

  1. 性质
  • 它本身是一个模块,可以是一个空文件
  • 这个模块的模块名不是__init__,而是这个包的名字,也就是装着__init__.py文件的文件夹的名字
  • 作用是将一个文件夹变为一个Python模块
  • 它可以不包含代码,不过一般会包含一些Python初始化代码(例如批量导入我们需要用到的模块,这样我们就不用在用到的时候再一一导入),在这个包被import的时候,这些代码会自动被执行【查看示例①】
  • 以from 包 import * 导入时,包内的模块是受__init__.py限制的。import会把在__init__.py文件中注册的包的子模块和子包导入到当前作用域中来,如果包中某个模块不在__init__.py文件中,会报错【查看示例②】
示例①

  如上述 模块导入示例, 为了使用arithmetic中的某个子模块(main.py中我们展示了四种不同的方式),必须使用非常繁琐地import语句;在使用的时候,也有可能需要使用非常繁琐的表达式;如果在不同的地方使用arithmetic的子模块,都需要写这么繁琐的import或者使用表达式这就是为什么需要利用__init__.py来简化使用

添加__init__.py文件,文件目录结构如下

|-main.py
|-arithmetic
	|-__init__.py
    |-add.py
    |-sub.py
    |-mul.py
    |-mul.py

在__init__.py中编写如下代码:

#
# @file __init__.py
#
import arithmetic.add
import arithmetic.sub
import arithmetic.mul
import arithmetic.dev

add = arithmetic.add.add
sub = arithmetic.sub.sub
mul = arithmetic.mul.mul
dev = arithmetic.dev.dev

同样,对main.py进行了一些适当的修改。

#
# @file main.py
#

import arithmetic as a4

def letscook(x, y, oper):
    r = 0
    if oper == "+":
        r = a4.add(x, y)
    elif oper == "-":
        r = a4.sub(x, y)
    elif oper == "*":
        r = a4.mul(x, y)
    else:
        r = a4.dev(x, y)

    print("{} {} {} = {}".format(x, oper, y, r))

x, y = 3, 8

letscook(x, y, "+")
letscook(x, y, "-")
letscook(x, y, "*")
letscook(x, y, "/")

  在__init__.py中, import了arithmetic下的所有子模块,并在__init__.py中给各个子模块的核心功能取了新的名字,作为arithmetic模块的变量。所以在main.py中import了arithmetic模块之后,就可以直接进行使用了。如果使用from arithmetic import * 语句,那么就可以使用add、sub、mul、dev,连a4都省了。
  另外,若模块中含有 __all__变量,以“from 模块名 import *”的形式导入该模块时,该文件中只能使用__all__ 列表中指定的成员。(注意!__all__ 变量仅限于在其它文件中以“from 模块名 import *”的方式引入,即__all__ 变量对“import 模块名”、 “from 模块名 import 对象成员”不起作用)

__ all__ 变量:该变量的值是一个列表,存储的是当前模块中一些成员(变量、函数或者类)的名称。

沿用上例,修改__ init__.py如下:

#
# @file __init__.py
#
import arithmetic.add
import arithmetic.sub
import arithmetic.mul
import arithmetic.dev

add = arithmetic.add.add
sub = arithmetic.sub.sub
mul = arithmetic.mul.mul
dev = arithmetic.dev.dev

__all__ = ["add","sub","mul"]

修改main.py文件如下:

#
# @file main.py
#

from arithmetic import *

def letscook(x, y, oper):
    r = 0
    if oper == "+":
        r = add(x, y)
    elif oper == "-":
        r = sub(x, y)
    elif oper == "*":
        r = mul(x, y)
    else:
        r = dev(x, y)

    print("{} {} {} = {}".format(x, oper, y, r))

x, y = 3, 8

letscook(x, y, "+")
letscook(x, y, "-")
letscook(x, y, "*")
letscook(x, y, "/")

  运行main.py文件,执行结果如下:NameError: name ‘dev’ is not defined
  注意:__ all__ 是模块中提供的变量,如同 __ name__,不仅可以在__ init__.py中运用,也可以在其他普通的模块中运用,因为__ init__.py本身就是一个模块。

示例②

文件目录结构如下

|-main.py
|-arithmetic
	|-__init__.py
    |-add.py
    |-sub.py
    |-mul.py
    |-mul.py

在__init__.py中编写如下代码:

#
# @file __init__.py
#
import arithmetic.add
import arithmetic.sub
import arithmetic.mul

add = arithmetic.add.add
sub = arithmetic.sub.sub
mul = arithmetic.mul.mul

main.py文件如下:

#
# @file main.py
#

from arithmetic import *

def letscook(x, y, oper):
    r = 0
    if oper == "+":
        r = add(x, y)
    elif oper == "-":
        r = sub(x, y)
    elif oper == "*":
        r = mul(x, y)
    else:
        r = arithmetic.dev.dev(x, y)

    print("{} {} {} = {}".format(x, oper, y, r))

x, y = 3, 8

letscook(x, y, "+")
letscook(x, y, "-")
letscook(x, y, "*")
letscook(x, y, "/")

  在__init__.py中, import了arithmetic下的add、sub、mul子模块,并在__init__.py中给各个子模块的核心功能取了新的名字,作为arithmetic模块的变量。但是没有导入arithmetic的dev模块,因此使用此模块的dev函数时需要用完整路径 arithmetic.dev.dev 进行验证。运行main.py文件,执行结果如下:

3 + 8 = 11
3 - 8 = -5
3 * 8 = 24
Traceback (most recent call last):
  File "D:\main.py", line 21, in <module>
    letscook(x, y, "/")
  File "D:\main.py", line 12, in letscook
    r = arithmetic.dev.dev(x, y)
AttributeError: module 'arithmetic' has no attribute 'dev'
  1. __ init__.py的设计原则
  • 不要污染现有的命名空间。模块一个目的,是为了避免命名冲突,如果你在种用__init__.py时违背这个原则,是反其道而为之,就没有必要使用模块了。
  • 利用__init__.py对外提供类型、变量和接口,对用户隐藏各个子模块的实现。一个模块的实现可能非常复杂,你需要用很多个文件,甚至很多子模块来实现,但用户可能只需要知道一个类型和接口。就像我们的arithmetic例子中,用户只需要知道四则运算有add、sub、mul、dev四个接口,却并不需要知道它们是怎么实现的,也不想去了解arithmetic中是如何组织各个子模块的。由于各个子模块的实现有可能非常复杂,而对外提供的类型和接口有可能非常的简单,我们就可以通过这个方式来对用户隐藏实现,同时提供非常方便的使用。
  • 只在__init__.py中导入有必要的内容,不要做没必要的运算。如示例①,import arithmetic语句会执行__ini__.py中的所有代码。如果我们在__init__.py中做太多事情,每次import都会有额外的运算,会造成没有必要的开销。

参考

知乎问答:python中的模块、库、包有什么区别
知乎问答:__ init__.py的神奇用法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值