组织结构:包、模块、类
- 顶级组织结构:包
- 第二层级:模块
- 第三层级:类(包含函数、变量)
1 | package1 包 |
- 若想访问某个文件夹下的某个文件,则可通过
包.文件名
来区分,也称为命名空间。例如:package1.c1
和package2.c1
分别访问了package1
文件夹下和package2
文件夹下的名为c1.py
的文件。 - 可以存在子包,子包和模块也可以是平行关系。
- 包和普通文件夹的区别:包里含有特定文件:
__init__.py
。 __init__.py
文件的访问方式:不是package1.__init__.py
,而是通过包名,package1
访问的即是__init__.py
文件。
导入模块
如果想在另一个文件引入其他模块,有如下两种方式:
1 | # 方法一:导入模块,但无法导入具体变量?? |
举个栗子,假设package1
中的c3.py
文件包含以下内容:
1 | a = 1 |
引入c3.py
文件的方式为:
1 | # 方法一 |
- 只用
import
不能在时导入模块中的变量,只能导入模块。缺点是命名空间过长。 - 不推荐使用
import *
,导入的变量不明确,且编辑器会因找不到具体的引入变量而提示错误,但代码可以运行。 import *
无法导入所有的变量(由内置变量决定)
如在导入变量和模块时需要换行,则可以通过/
或()
进行导入。
1 | # 写法一 |
模块的内置变量
all
package1中的c3.py包含以下内容:
1 | __all__ = ['a','c'] |
当 首先自动执行 __init__.py
,__init__.py
通常做初始化
1 | package1 |
内置变量
内置变量通常为前后双下划线格式,与我们定义的变量含义相同,只是由系统定义的。
dir()
dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。
1 | ### 无参数 |
常见内置变量
__name__
:当前模块的完整路径(命名空间)__package__
:当前文件所在模块__file__
:系统完整的物理路径__doc__
:模块的注释
举个栗子:
package1-c1.py
1 | ''' |
package2-c1.py
1 | import package1.c1 |
如果我们在package2的c1.py文件中打印相同的内容:
package2 c1.py
1 | print('name: '+__name__) |
更推荐的写法为:
package2 c1.py
1 | print('name: '+__name__) |
package2和package1中的c1.py均执行以后,打印出的结果完全不一样。package1. c1 被当做应用程序的入口文件执行的,而package2被当做普通的模块。入口文件的内置变量是不同的。
- 当文件被当做入口文件,则
__name__
会打印出__main__
- 当文件被当做入口文件,则顶级没有包(尽管看起来有package2)
如何将可执行文件当作一个模块来使用呢?在python命令中通过-m
命令即可。前提是必须具备当作模块的条件。
1 | python -m package2.c1 |
注:当需要把py文件作为普通模块,必须有包。因此必须进入到package目录,执行package底下的python文件才可以。
__name__的应用
关于__name__
,有一句比较经典的话:
Make a script both importable and executable
说白了,就是让你的脚本既可以作为普通模块提供给其他程序调用,也可以作为可执行的文件。脚本不同的两种角色,会有不同的使用方式。下面的代码判断是否为入口文件:
1 | if __name__ == '__main__': |
以下面的代码为例:
c1.py
1 | if __name__ == '__main__': |
c2.py1
import c1
如果执行c1.py
文件,则说明c1为入口文件,输出的结果如下:
1 | This is app |
如果执行c2.py
文件,则说明c1为引入模块,输出的结果如下:
1 | This is a module |
相对导入与绝对导入
常规目录结构:
1 | DEMO(顶级包) |
绝对导入
绝对引入必须从顶级包开始。
事实上,DEMO不一定为顶级包,与入口文件的位置有关。例如main.py
不属于任何包
main.py
1 | print(__package__) |
如果改成下面的写法:
m2.py
1 | print(__package__) |
main.py
1 | import package2.package4.m2 |
此时,对于m2.py
来讲,顶级包为package2
,并非DEMO
。这是由于可执行文件main.py
与package2
文件夹同级。因此,哪个文件夹为顶级包是由可执行文件(入口文件)决定的。
如果将main.py
移动到DEMO
文件夹同级,则m2.py
的顶级包的位置也将修改。必须将main.py
修改为:
main.py
1 | import demo.package2.package4.m2 |
相对导入
import不可以使用相对路径,但使用from…import时可使用相对路径。
相对导入不能导入超过当前文件顶级包的文件。
入口文件无法使用相对路径导入。
.
代表当前目录,..
代表上一级目录,...
代表上上级目录,以此类推。但是在可执行文件中,不能使用相对路径导入。
main.py
1 | import .package2.package4.m2 |
在上面的例子中,如果在import中使用相对路径,则会报语法错误。
m2.py
1 | m = 2 |
main.py
1 | from .package2.package4.m2 import m |
在上面的例子中,使用from...import
不再报语法错误。修改为如下目录结构:
1 | DEMO(顶级包) |
如果在 m2.py
中分别做如下引入,均为正确:
m2.py
1 | import .m3 #正确 |
分别设置import .m3
和import ..m4
时执行main.py
文件,得到的结果为:
main.py
1 | #import .m3 |
尝试引入更高层级的包,执行m2.py
,则结果会报错。
m2.py
1 | import ...m5 |
因此,当试图引入超过顶级包的文件(包),则会报错。由于m2
的顶级包为package2
,当import ...m5
时,已超过顶级包的位置,因此会报错。
为什么不能在入口文件中使用相对路径?
相对路径时根据当前的内置变量__name__
来定位,当入口文件__name__ ==__main__
时,__name__
不存在的。因此不能使用相对路径。
如何在入口文件中使用相对路径?
进入入口文件的上一层目录,执行带-m
的python
命令。
1 | cd .. |