当前位置: 首页 > article >正文

Python自学 - “包”的创建与使用(从头晕到了然)

<< 返回目录

1 Python自学 - “包”的创建与使用(从头晕到了然)

  相对于模块是一个更大的概念,按照业界的开发规范,1个代码文件不要超过1000行,稍微有点规模的任务就超过这个代码限制了,必然需要多个文件来管理,对于这种包含多个代码文件的模块集,我们一般通过单独新建文件夹来管理,而这个文件夹及里面的文件,我们就称之为包。
  包里面还可以放文件夹,子文件夹里面还可以放代码文件。

  • 约束
    • 包的文件夹里面必须要有__init__.py文件,子文件夹也需要有,该文件可以是空文件,也可以包含python代码(初始化包级别的变量、导入模块或子包等),当执行import语句时,包中的__init__.py会执行。

1.1 创建包

  假设我们要构造一个人类社会的包,包名叫social,在social中还会包含多个子包:人(human)、学生(student)、工人(worker)…

目录结构如下:

social
├── __init__.py
├── human
│   ├── __init__.py
│   └── human.py
├── student
│   ├── __init__.py
│   └── student.py
└── worker
    ├── __init__.py
    └── worker.py

1.2 使用包

  本节主要讨论__init__.py文件内容对包使用方式的影响,主要涉及包的导入路径。

1.2.1 __init__.py全空时的使用方式

  包中所有目录包括子目录下的__init__.py文件全部为空

  • 示例1:__init__.py全空时包的使用方式
import social.human.human
h1 = social.human.human.Human('小飞棍', 22, '男')
print(h1)

输出:

Human, name:小飞棍, age:22, gender:男.

点评:这种方式,在使用对象Human时,需要输入一长串路径,非常麻烦,并不是一种好的方式
问题为什么使用Human对象的命名空间如此之深?
  我们对social.human.human.Human拆分从前到后理解如下:

  • social: 表示包的根目录(或者说包名,本质还是目录名)
  • human:对应social目录下的子目录human
  • human:对应子目录human下的human.py文件。
  • Human:对应human.py文件中的class Human

请读者回头阅读前文中的目录结构!

  • 示例2:__init__.py全空时包的使用方式-改进
import social.human.human as H
h1 = H.Human('小飞棍', 22, '男')
print(h1)

输出:

Human, name:小飞棍, age:22, gender:男.

点评:在导入时使用了别名, 将一长串的模块名,指定别名H,在使用对象时,只需要使用别名H来指定命名空间。

  • 示例3:__init__.py全空时包的使用方式-改进2
      本示例中,使用from关键字导入对象,大家关注路径:social.human.human直接指定到了human.py文件,所以此时import *已经可以够得着Human对象了,因此,在代码中可以直接使用Human声明对象。
from social.human.human import *
h1 = Human('小飞棍', 22, '男')
print(h1)

输出:

Human, name:小飞棍, age:22, gender:男.

1.2.2 包的根目录下的__init__.py加上导入子包语句

  包social根目录下的__init__.py使用import导入子包,而子包下的__init__.py仍然为空。

social根目录下的__init__.py文件内容:

from . import human
from . import student
from . import worker

这里的"."是相对路径, 因为social目录下就是子包的目录名human

  • 示例4:包的根目录__init__.py内容完整时的包使用示例
import social
h1 = social.human.human.Human('小飞棍', 22, '男')
print(h1)

输出:

Human, name:小飞棍, age:22, gender:男.

点评差评!必须差评!__init__.py为空时使用Human对象就这么一长串:social.human.human.Human__init__.py写了import语句,还要这么长一串,那__init__.py不是白写了!
  亲,我知道你很急,但是请你先不要急!我们来掰扯一下!
social目录下执行了from . import human,这里的from .中的“.”表示当前目录,本质还是social,然后导入了human只是social下的子目录human,所以,在social的命名空间,social.human只能够到social目录下的子目录human
要够到human.py文件,还要再加一层.human,最后才能够到Human类。
但是你得到了一个好处,import social这句不是短了?!

  • 示例5:根目录的__init__.py导入时多走一层
    social根目录下的__init__.py文件内容:
from .human import human  #只改human子包引用方式,以示区别
from . import student
from . import worker

包的导入及使用代码(如果代码不变):

import social
h1 = social.human.human.Human('小飞棍', 22, '男')
print(h1)

输出:

Traceback (most recent call last):
  File "D:\TYYSOFT\Study\Python\test_package.py", line 2, in <module>
    h1 = social.human.human.Human('小飞棍', 22, '男')
AttributeError: module 'social.human.human' has no attribute 'human'

点评__init__.py已经多走了一步,但是执行代码中没有退一步,踩到脚了,出错了!

正确的导入及使用代码:

import social   #这里已经够到了human.py
h1 = social.human.Human('小飞棍', 22, '男')
print(h1)

输出:

Human, name:小飞棍, age:22, gender:男.

点评:因为在__init__.py中执行了from .human import humanfrom .human语句中,“.”表示当前目录(social),然后接着是子目录(human),from这半句已经进入到了social/human子目录,后半句import human相当于把human.py已经拿到了social命名空间下,因此:social.human已经够到了human.py文件!
顺理成章的social.human.Human就够到了Human类。

1.2.3 根目录下的__init__.py和子目录下的__init__.py关系

  读者可能会有疑惑,根目录下有__init__.py文件,子目录下也有__init__.py文件,那这些__init__.py文件有什么关系呢?
一切与import语句强相关:
import social: 执行包目录social下的__init__.py文件
import social.human: 执行目录social/human下的__init__.py文件

1.2.4 子包引用子包

  子包student中的Student类需要继承Human类,因此,需要导入human.py模块

student.py代码示例:

from social.human.human import *
class Student(Human):
    def __init__(self, name, age, gender, grade):
        self.grade = grade
        super().__init__(name, age, gender)

    def __str__(self):
        return f"Student: (name: {self.name}, age: {self.age}, gender: {self.gender}, grade: {self.grade})"

注:这里使用了绝对路径,也可以使用相对路径:from ..human.human import *

主程序代码:

import social.student.student as ST
std = ST.Student("小飞棍", 18, '男', '9')
print(std)

输出:

Student: (name: 小飞棍, age: 18, gender: 男, grade: 9)

1.2.5 对from social import *的定制

  from xxx import *一般会把xxx包下的所有对象都导入到当前命名空间,那如果想隐藏某些细节,或仅仅是为了防止对象冲突该怎么办呢?

此时会用到__init__.py文件中的变量__all__
__init__.py示例:

from . import human
from . import student
from . import worker

__all__ = ['human', 'student']

读者请注意:__all__变量只填写了两个模块:humanstudent,而没有worker

执行代码:

from social import *
print(dir(human))
print(dir(student))
print(dir(worker))

输出:

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'human']
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
Traceback (most recent call last):
  File "D:\TYYSOFT\Study\Python\test_package.py", line 4, in <module>
    print(dir(worker))
NameError: name 'worker' is not defined

点评:代码报错, 提示worker找不到!

1.3 总结

  • import到哪个目录,就执行哪个目录的__init__.py文件。
  • __init__.py中的导入语句要和业务代码中的import语句保持一致,不要踩到脚!
🕮说明
  本文中的`__init__.py`文件中的`import`都使用了相对路径,`Python`也支持绝对路径,如:`from social.human.human import *`

作者声明:本文用于记录和分享作者的学习心得,可能有部分文字或示例来源自豆包AI,由于本人水平有限,难免存在表达错误,欢迎留言交流和指教!
Copyright © 2022~2025 All rights reserved.

<< 返回目录


http://www.kler.cn/a/503471.html

相关文章:

  • 论文笔记(四十七)Diffusion policy: Visuomotor policy learning via action diffusion(下)
  • 使用Deepseek搭建类Cursor编辑器
  • 【Docker】Docker部署多种容器
  • 嵌入式系统中的 OpenCV 与 OpenGLES 协同应用
  • vue2修改表单只提交被修改的数据的字段传给后端接口
  • CVE-2025-22777 (CVSS 9.8):WordPress | GiveWP 插件的严重漏洞
  • 电子邮件安全及核心概念
  • 探索AI与鸿蒙开发新领域:从《星火AI使用指南》到《鸿蒙应用开发宝典》
  • 远程连接不上怎么回事?
  • HTML5 滚动动画详解
  • 常见的php框架有哪几个?
  • 利用Java爬虫按图搜索1688商品(拍立淘)的实践指南
  • npm install 报错常见的解决方法
  • 论文阅读:SplatMAP: Online Dense Monocular SLAM with 3D Gaussian Splatting
  • 解决VMWare虚拟机“无法获取vmci驱动程序版本”的问题
  • 如何应对突然忘记 MySQL 登录密码的情况?
  • 宁德时代C++后端开发面试题及参考答案
  • 【DevOps】Jenkins配置钉钉邮件通知
  • Certificates do not conform to algorithm constraints
  • 【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发二
  • 图形和动画本地化
  • 微信小程序:播放音频
  • 新垂直电商的社交传播策略与AI智能名片2+1链动模式S2B2C商城小程序的应用探索
  • 用css和html制作太极图
  • (EMNLP-2023)预训练语言模型的稀疏低秩自适应
  • Joplin Server配置端口转发的一个小坑