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

什么是包

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

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

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

图 1 numpy 模块(包)的文件结构

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

定义包

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

创建一个文件夹,该文件夹的名字就是该包的包名。

在该文件夹内添加一个 __init__.py 文件即可。

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

'''

这是学习包的第一个示例

'''

print('this is first_package')

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

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

# 导入first_package包(模块)

import first_package

print('==========')

print(first_package.__doc__)

print(type(first_package))

print(first_package)

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

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

this is first package

==========

这是学习包的第一个示例

从上面的输出结果可以看出,在导入 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 模块文件的内容如下:

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 = price

def __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_chart

fk_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 文件编辑成如下形式:

# 从当前包导入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包及其定义和引用详解相关推荐

  1. python标准类型内建模块_Python内建模块struct实例详解

    本文研究的主要是Python内建模块struct的相关内容,具体如下. Python中变量的类型只有列表.元祖.字典.集合等高级抽象类型,并没有像c中定义了位.字节.整型等底层初级类型.因为Pytho ...

  2. python分析方向的第三方库_Python标准库与第三方库详解

    干货大礼包!21天带你轻松学Python(文末领取更多福利) 点击查看课程视频地址 本课程来自于千锋教育在阿里云开发者社区学习中心上线课程<Python入门2020最新大课>,主讲人姜伟. ...

  3. python脚本运行时网络异常_Python中异常重试的解决方案详解

    前言 大家在做数据抓取的时候,经常遇到由于网络问题导致的程序保存,先前只是记录了错误内容,并对错误内容进行后期处理. 原先的流程: def crawl_page(url): pass def log_ ...

  4. python数据清理的实践总结_python 数据的清理行为实例详解

    python 数据的清理行为实例详解 数据清洗主要是指填充缺失数据,消除噪声数据等操作,主要还是通过分析"脏数据"产生的原因和存在形式,利用现有的数据挖掘手段去清洗"脏数 ...

  5. python爬虫多线程是什么意思_python爬虫中多线程的使用详解

    queue介绍 queue是python的标准库,俗称队列.可以直接import引用,在python2.x中,模块名为Queue.python3直接queue即可 在python中,多个线程之间的数据 ...

  6. 基于python的随机森林回归实现_PYTHON | 随机森林实战(代码+详解)

    大家好,我是菜鸟君,之前跟大家聊过R语言的随机森林建模,指路 R语言 | 随机森林建模实战(代码+详解),作为刚过完1024节日的码农算法工程师来说,怎么可能只会用一种语言呢?今天就来说说Python ...

  7. python源程序文件的扩展名_python程序文件扩展名知识点详解

    python程序文件的扩展名称是什么 python程序的扩展名有.py..pyc..pyo和.pyd..py是源文件,.pyc是源文件编译后的文件,.pyo是源文件优化编译后的文件,.pyd是其他语言 ...

  8. python中values是什么意思_Python values()与itervalues()的用法详解

    dict 对象有一个 values() 方法,这个方法把dict转换成一个包含所有value的list,这样,我们迭代的就是 dict的每一个 value: d = { 'Adam': 95, 'Li ...

  9. python for i in range(len())_Python for i in range ()用法详解

    for i in range ()作用: range()是一个函数, for i in range () 就是给i赋值: 比如 for i in range (1,3): 就是把1,2依次赋值给i r ...

最新文章

  1. 远程拷贝代码 指定端口
  2. 多进程,守护进程,锁
  3. 将所有的表中,数值类型由char,varchar改为nchar,nvarchar 的存储过程
  4. 图论(二)--各种图介绍
  5. gradle安装及idea导入spring5.0x的源码
  6. Struts 学习笔记之ActionForm
  7. Git正解 脱水版 【10. 内部机制】
  8. On the eighth day
  9. MySQL 10060错误 解决方法
  10. 【嵌入式】STM32实现SPI双机通信的一些细节(2)片选总结
  11. 测试工作规范及岗位职责
  12. IT职场人生系列之二十三 知识体系(专家与杂家)
  13. 2017 计蒜之道 初赛 第一场 A 阿里的新游戏
  14. 网聊是不是就要劈腿上床?
  15. macOS系统下载和配置git教程
  16. HCIP第十天 交换第一天
  17. java聊天室测试_Java网络聊天室实训能力测试
  18. java 函数fun_c语言中fun用法详解_后端开发
  19. 游戏原创声音该如何鉴定呢?
  20. 无聊的时候写了个颜色识别算法,基于RGB颜色模型

热门文章

  1. python colormap函数_python-使用由x,y位置定义的rgb值创建colormap
  2. 基于长短读长和参考基因组的组装错误检测算法的研究
  3. 如何进行基因组组装?
  4. 第四章 遗传变异的分类
  5. Evaluation and Validation of AssemblingCorrected PacBio Long Reads for MicrobialGenome Completion
  6. html中hover有静止的命令,我可以通过JavaScript禁用CSS:hover效果吗?
  7. linux 后台运行jar包命令,Linux 运行jar包命令(Cent OS 7后台运行jar包)
  8. torch==1.1.0和torchvision-0.3.0安装
  9. 12.前K个高频元素---使用优先队列和哈希表解决
  10. 注册HttpSessionListener失效原因