<< 返回目录
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 human
,from .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__
变量只填写了两个模块:human
和student
,而没有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.
<< 返回目录