cpython包_Python包及其定义和引用详解

2023-10-08 09:59

本文主要是介绍cpython包_Python包及其定义和引用详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Python包及其定义和引用详解

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

什么是包

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

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

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

b2b8adc278919ff256c5aa3c27d100f1.gif

图 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.前缀来访问三个模块内的程序单元。

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

这篇关于cpython包_Python包及其定义和引用详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/164502

相关文章

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4