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

news/2025/1/15 17:39:30/

<< 返回目录

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 = gradesuper().__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.ppmy.cn/news/1563380.html

相关文章

特制一个自己的UI库,只用CSS、图标、emoji图 第二版

图&#xff1a; 代码&#xff1a; index.html <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>M…

使用 Java 操作 Excel 的实用教程

&#x1f496; 欢迎来到我的博客&#xff01; 非常高兴能在这里与您相遇。在这里&#xff0c;您不仅能获得有趣的技术分享&#xff0c;还能感受到轻松愉快的氛围。无论您是编程新手&#xff0c;还是资深开发者&#xff0c;都能在这里找到属于您的知识宝藏&#xff0c;学习和成长…

Linux(CentOS7)安装JDK和Maven

文章目录 CentOS软件安装方式JDK安装Maven安装 CentOS软件安装方式 安装方式特点二进制发布包安装软件已经针对具体平台编译打包发布&#xff0c;只要解压&#xff0c;修改配置即可。例如tomcatrpm(redhat package manager)安装软件已经按照redhat的包管理规范进行打包&#x…

.NET内网实战:反射实现Rundll32绕过防护

01阅读须知 此文所节选自小报童《.NET 内网实战攻防》专栏&#xff0c;主要内容有.NET在各个内网渗透阶段与Windows系统交互的方式和技巧。 02基本介绍 本文内容部分节选自小报童《.NET 通过反射技术实现Rundll32功能绕过安全防护》&#xff0c;目前已有280位朋友抢先预定&a…

matlab使用 BP 神经网络进行数据预测的完整流程,包括数据读取、数据预处理等等

%% 初始化程序 warning off % 关闭报警信息 close all % 关闭所有图窗 clear % 清空变量 clc % 清空命令行 setdemorandstream(172) %设置随机种子为1%% 读取数据 data xlsread(Y.xlsx); %% 划分训练集…

前端网页开发学习(HTML+CSS+JS)有这一篇就够!

目录 HTML教程 ▐ 概述 ▐ 基础语法 ▐ 文本标签 ▐ 列表标签 ▐ 表格标签 ▐ 表单标签 CSS教程 ▐ 概述 ▐ 基础语法 ▐ 选择器 ▐ 修饰文本 ▐ 修饰背景 ▐ 透明度 ▐ 伪类 ▐ 盒子模型 ▐ 浮动 ▐ 定位 JavaScript教程 ▐ 概述 ▐ 基础语法 ▐ 函数 …

Java配置log4j日志打印

1. 引入依赖 <dependencies><!-- Log4j 2依赖 --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>1.2.14</version> <!-- 可以根据需要修改版本 --></…

[大模型]本地离线运行openwebui+ollama容器化部署

本地离线运行Openweb-ui ollama容器化部署 说明安装internet操作内网操作 问题线程启动错误最终命令 总结 说明 最近公司有一个在内网部署一个离线大模型的需求&#xff0c;网络是离线状态&#xff0c;服务器有A100GPU&#xff0c;一开始是想折腾开源chatGML4大模型&#xff0…