对于一个需要实际应用的模块而言,往往会具有很多程序单元,包括变量、函数和类等,如果将整个模块的所有内容都定义在同一个 Python 源文件中,这个文件将会变得非常庞大,显然并不利于模块化开发。

什么是包

为了更好地管理多个模块源文件,Python 提供了包的概念。那么问题来了,什么是包呢?

从物理上看,包就是一个文件夹,在该文件夹下包含了一个 init.py 文件,该文件夹可用于包含多个模块源文件;从逻辑上看,包的本质依然是模块。

根据上面介绍可以得到一个推论,包的作用是包含多个模块,但包的本质依然是模块,因此包也可用于包含包。典型地,当我们为 Python 安装了 numpy 模块之后,可以在 Python 安装目录的 Lib\site-packages 目录下找到一个 numpy 文件夹,它就是前面安装的 numpy 模块(其实是一个包)。该文件夹的内容如下图所示:

从图 1 可以看出,在 numpy 包(也是模块)下既包含了 matlib.py 等模块源文件,也包含了 core 等子包(也是模块)。这正对应了我们刚刚介绍的:包的本质依然是模块,因此包又可以包含包。

定义包

掌握了包是什么之后,接下来学习如何定义包。定义包更简单,主要有两步:

  • 创建一个文件夹,该文件夹的名字就是该包的包名。
  • 在该文件夹内添加一个 init.py 文件即可。

下面定义一个非常简单的包。先新建一个 first_package 文件夹,然后在该文件夹中添加一个 init.py 文件,该文件内容如下:

'''
这是学习包的第一个示例
'''
print('this is first_package')

上面的 Python 源文件非常简单,该文件开始部分的字符串是该包的说明文档,接下来是一条简单的输出语句。

下面通过如下程序来使用该包:

# 导入first_package包(模块)
import first_packageprint('==========')
print(first_package.__doc__)
print(type(first_package))
print(first_package)

再次强调,包的本质就是模块,因此导入包和导入模块的语法完全相同。因此,上面程序中第 2 行代码导入了 first_package 包。程序最后三行代码输出了包的说明文档、包的类型和包本身。

运行该程序,可以看到如下输出结果:

this is first package
==========
这是学习包的第一个示例
<class 'module'>
<module 'first_package' from 'G:\\publish\\codes\\09\\9.3\\first_package\\__init__.py'>

从上面的输出结果可以看出,在导入 first_package 包时,程序执行了该包所对应的文件夹下的 init.py;从倒数第二行输出可以看到,包的本质就是模块;从最后一行输出可以看到,使用 import
first_package 导入包的本质就是加载井执行该包下的 init.py 文件,然后将整个文件内容赋值给与包同名的变量,该变量的类型是 module。

与模块类似的是,包被导入之后,会在包目录下生成一个 pycache 文件夹,并在该文件夹内为包生成一个 init.cpython-36.pyc 文件。

由于导入包就相当于导入该包下的 init.py 文件,因此我们完全可以在 init.py 文件中定义变量、函数、类等程序单元,但实际上往往并不会这么做。想一想原因是什么?包的主要作用是包含多个模块,因此 init.py 文件的主要作用就是导入该包内的其他模块。

下面再定义一个更加复杂的包,在该包下将会包含多个模块,并使用 init.py 文件来加载这些模块。

新建一个fk_package包,并在该包下包含三个模块文件:

  • print_shape.py
  • billing.py
  • arithmetic_chart.py

fk_package 的文件结构如下:

fk_package
┠──arithmetic_chart.py
┠──billing.py
┠──print_shape.py
┗━━__init__.py

其中,arithmetic_chart.py模块文件的内容如下:

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:725638078
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def print_multiple_chart(n):'打印乘法口角表的函数'for i in range(n):for j in range(i + 1):print('%d * %d = %2d' % ((j + 1) , (i + 1) , (j + 1)* (i + 1)), end='  ')print('')

上面模块文件中定义了一个打印乘法口诀表的函数。

billing.py 模块文件的内容如下:

class Item:'定义代表商品的Item类'def __init__(self, price):self.price = pricedef __repr__(self):return 'Item[price=%g]' % self.price

print_shape.py 模块文件的内容如下:

def print_blank_triangle(n):'使用星号打印一个空心的三角形'if n <= 0:raise ValueError('n必须大于0')for i in range(n):print(' ' * (n - i - 1), end='')print('*', end='')if i != n - 1:print(' ' * (2 * i - 1), end='')else:print('*' * (2 * i - 1), end='')if i != 0:print('*')else:print('')

tk_package包下的 init.py 文件暂时为空,不用编写任何内容。

上面三个模块文件都位于fk_package包下,总共提供了两个函数和一个类。这意味着 fk_package 包(也是模块)总共包含arithmetic_chart、 billing 和 print_shape三个模块。在这种情况下,这三个模块就相当于fk_package 包的成员。

导入包内成员

如果需要使用 arithmetic_chart、 billing 和 print_shape 这三个模块,则可以在程序中执行如下导入代码:

# 导入fk_package包,实际上就是导入包下__init__.py文件
import fk_package
# 导入fk_package包下的print_shape模块,
# 实际上就是导入fk_package目录下的print_shape.py
import fk_package.print_shape
# 实际上就是导入fk_package包(模块)导入print_shape模块
from fk_package import billing
# 导入fk_package包下的arithmetic_chart模块,
# 实际上就是导入fk_package目录下的arithmetic_chart.py
import fk_package.arithmetic_chartfk_package.print_shape.print_blank_triangle(5)
im = billing.Item(4.5)
print(im)
fk_package.arithmetic_chart.print_multiple_chart(5)

上面程序中第 2 行代码是“import fk_package”,由于导入包的本质只是加载并执行包里的 init.py 文件,因此执行这条导入语句之后,程序只能使用 fk_package 目录下的 init.py 文件中定义的程序单元。对于本例而言,由于 fk_package_init_.py 文件内容为空,因此这条导入语句没有任何作用。

第 5 行导入语句的本质就是加载并执行 fk_package 包下的 print_shape.py 文件,并将其赋值给 fk_package.print_shape 变量。因此执行这条导入语句之后,程序可访问 fk_package\print_shape.py 文件所定义的程序单元,但需要添加 fk_package.print_shape 前缀。

第 8 行导入语句的本质是导入 fk_package 包(也是模块)下的 billing 成员(其实是模块)。因此执行这条导入语句之后,程序可使用 fk_package\billing.py 文件定义的程序单元,而且只需要添加 billing 前缀。

第 11 行代码与第 5 行代码的导入效果相同。

该程序后面分别测试了 fk_package包下的 print_shape、billing、arithmetic_chart 这三个模块的功能。运行上面程序,可以看到三个模块的功能完全可以正常显示。

上面程序虽然可以正常运行,但此时存在两个问题:

  • 为了调用包内模块中的程序单元,需要使用很长的前缀,这实在是太麻烦了。
  • 包内 init.py 文件的功能完全被忽略了。

想一想就知道,包内的 init.py 文件并不是用来定义程序单元的,而是用于导入该包内模块的成员,这样即可把模块中的成员导入变成包内成员,以后使用起来会更加方便。

fk_package包下的 init.py 文件编辑成如下形式:

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:725638078
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
# 从当前包导入print_shape模块
from . import print_shape
# 从.print_shape导入所有程序单元到fk_package中
from .print_shape import *
# 从当前包导入billing模块
from . import billing
# 从.billing导入所有程序单元到fk_package中
from .billing import *
# 从当前包导入arithmetic_chart模块
from . import arithmetic_chart
# 从.arithmetic_chart导入所有程序单元到fk_package中
from .arithmetic_chart import *

该程序的代码基本上差不多,都是通过如下两行代码来处理导入的:

# 从当前包导入print_shape模块
from . import print_shape
# 从.print_shape导入所有程序单元到fk_package中
from .print_shape import *

上面第一行 from…import 用于导入当前包(模块)中的 print_shape(模块),这样即可在 tk_package 中使用 print_shape 模块。但这种导入方式是将 print_shape 模块导入了 fk_package 包中,因此当其他程序使用 print_shape 内的成员时,依然需要通过 fk_package.print_shape 前缀进行调用。

第二行导入语句用于将 print_shape 模块内的所有程序单元导入 fk_package 模块中,这样以后只要使用 fk_package.前缀就可以使用三个模块内的程序单元。例如如下程序:

# 导入fk_package包,实际上就是导入包下__init__.py文件
import fk_package# 直接使用fk_package前缀即可调用它所包含的模块内的程序单元。
fk_package.print_blank_triangle(5)
im = fk_package.Item(4.5)
print(im)
fk_package.print_multiple_chart(5)

上面第 2 行代码是导入 tk_package 包,导入该包的本质就是导入该包下的 init.py 文件。而 init.py 文件又执行了导入,它们会把三个模块内的程序单元导入 tk_package 包中,因此程序的下面代码可使用 tk_package.前缀来访问三个模块内的程序单元。

运行上面程序,同样可以看到正常的运行结果。

结尾给大家推荐一个非常好的学习教程,希望对你学习Python有帮助!

Python基础入门教程推荐:更多Python视频教程-关注B站:Python学习者
https://www.bilibili.com/video/BV1LL4y1h7ny?share_source=copy_web

Python爬虫案例教程推荐:更多Python视频教程-关注B站:Python学习者
https://www.bilibili.com/video/BV1QZ4y1N7YA?share_source=copy_web

Python中包含义及其定义相关推荐

  1. python怎么导入包-如何理解Python中包的引入

    Python的from import *和from import *,它们的功能都是将包引入使用,但是它们是怎么执行的以及为什么使用这种语法呢? 从一模块导入全部功能 from import * me ...

  2. python中[::]的含义

    python中[::]的含义 :是取值范围,','用来分割维度 需要注意的是list定义的二维数组不能用此方法取数值 最好使用nump定义的array类型 [:-1] b = [1,2,3,4,5,6 ...

  3. python中可变参数args传入函数时储存的类型是_[转载]Python中函数的参数定义和可变参数*args与**args...

    Python中函数的参数定义和可变参数 *args与**args区别 刚学用Python的时候,特别是看一些库的源码时,经常会看到func(*args, **kwargs)这样的函数定义,这个*和** ...

  4. python中的os.chdir()定义及作用

    其实关于python自带的os以及sys以及管理输入参数的arg这个包,我也不太清楚有什么实际用途,但是肯定用于路径管理,工作空间,模块导入相关.今天这篇文章我们来了解一下python中的os.chd ...

  5. Python中如何使用构造方法定义类

    每个类都有一个默认的__init__()方法,如果在定义类时显式地定义了__init__()方法,则创建对象时Python解释器会调用显式定义的__init__()方法;如果定义类时没有显式定义__i ...

  6. python中的方法需要定义_Python中规范定义命名空间的一些建议

    API的设计是一个艺术活.往往需要其简单.易懂.整洁.不累赘. 很多时候,我们在底层封装一个方法给高层用,而其它的方法只是为了辅助这个方法的. 也就是说我们只需要暴露这个方法就行,不用关心这个方法是怎 ...

  7. python中包的循环导入_具有继承的包中的循环导入依赖项

    这是一个可怕的Python循环导入参数,但是,IMHO,您可以有一个优秀的设计,仍然需要循环引用.在 所以,试试这个方法: 在东西.py公司名称:class Thing(Base): def acti ...

  8. 在python中、对于函数定义代码的理解_python中如何理解装饰器代码?

    长文预警,[最浅显易懂的装饰器讲解] 能不能专业地复制题目?配上代码,问题分段. 我来给提主配上问题的代码. 正式回答: 1:如何理解return一个函数,它与return一个值得用法区别在哪? 敲黑 ...

  9. python中形参可以使用中文定义嘛_python中函数的参数分类

    详解python函数的参数 一.参数的定义 1.函数的参数在哪里定义 在python中定义函数的时候,函数名后面的括号里就是用来定义参数的,如果有多个参数的话,那么参数之间直接用逗号, 隔开 案列: ...

最新文章

  1. 设计模式----组合模式UML和实现代码
  2. 金融类APP不得强制读取通讯录
  3. 【计算机网络】网络层 : BGP 协议 ( BGP 协议简介 | BGP 协议信息交换 | BGP 协议报文格式 | BGP-4 常用报文 | RIP 、OSPF、BGP 协议对比 )
  4. PL/SQL异常处理(原创)
  5. LeetCode 88. 合并两个有序数组(Merge Sorted Array)
  6. 美国36%流量背后 Netflix CDN分发算法优化
  7. 对比Excel,学习Python报表自动化实战
  8. XP硬盘安装Fedora14图文教程
  9. 最强Android教程!2021年Android面经分享,大厂面经合集
  10. Halcon算子学习:surface_normals_object_model_3d
  11. Blend_技巧篇_淡入淡出
  12. 监测到本计算机上装有sql,检测局域网电脑是否有安装SQL Server数据库
  13. Mybatis 逆向工程使用姿势不对文档全被清空,一怒之下写了个插件……
  14. [经验教程]拼多多购物车在哪里怎么加入购物车合并一起付款?
  15. 基于注意力机制超分辨率汇总
  16. linux系统软路由软件,使用Linux+Zebra构建软路由系统
  17. Cairo图形指南(6)
  18. My learning backups
  19. 招商银行信用卡中心21届实习笔试编程题
  20. 红警ol服务器维护中1003,不止于经典,全球唯一正版授权《红警OL》登录UP2018腾讯新文创生态大会...

热门文章

  1. js --- 递归结构图
  2. 2018中国互联网企业100强揭晓!阿里巴巴名列榜首
  3. qmake, makefile, make是什么东东,makefile简介!
  4. oracle 大字段clob检索
  5. 2009年全球最佳IT十大认证
  6. linux下uvc协议访问usb摄像头,Ubuntu调用USB摄像头
  7. java tm for chrome_java – 是否可以使用Postman Chrome扩展程序发送hashmap?
  8. 【VC基础】 4、变式表(Variant Table)
  9. 【MM模块】 Goods Receipt 收货 4
  10. 程序间数据共享与传递(1):EXPORT/IMPORT、SAP/ABAP Memory