您的位置:时时app平台注册网站 > 编程知识 > Python杂谈: __init__.py的作用【时时app平台注册网站

Python杂谈: __init__.py的作用【时时app平台注册网站

2019-10-18 18:10

1. 标识该目录是一个python的模块包(module package)

  如果你是使用python的相关IDE来进行开发,那么如果目录中存在该文件,该目录就会被识别为 module package 。

模块

模块,在Python可理解为对应于一个文件。在创建了一个脚本文件后,定义了某些函数和变量。你在其他需要这些功能的文件中,导入这模块,就可重用这些函数和变量。一般用module_name.fun_name,和module_name.var_name进行使用。这样的语义用法使模块看起来很像类或者名字空间,可将module_name 理解为名字限定符。模块名就是文件名去掉.py后缀。

client.py

def func():
    print "hello world!"

main.py

import client
if __name__ == '__main__':
    print __name__
    client.func()
    print client.__name__


>>python main.py  ---> result:

main

hello world!

client

模块属性__name__,它的值由Python解释器设定。如果脚本文件是作为主程序调用,其值就设为__main__,如果是作为模块被其他文件导入,它的值就是其文件名。

每个模块都有自己的私有符号表,所有定义在模块里面的函数把它当做全局符号表使用。

模块可以导入其他的模块。通常将import语句放在模块的开头,被导入的模块名字放在导入它的模块的符号表中。

from module import names 可以直接从模块中导入名字到符号表,但模块名字不会被导入。
from module import * 可以把模块中的所有名字全部导入,除了那些以下划线开头的名字符号。不建议使用,不清楚导入了什么符号,有可能覆盖自己定义的东西

内建函数dir()可以查看模块定义了什么名字(包括变量名,模块名,函数名等):dir(模块名),没有参数时返回所有当前定义的名字
模块搜索路径
当导入一个模块时,解释器先在当前包中查找模块,若找不到,然后在内置的built-in模块中查找,找不到则按sys.path给定的路径找对应的模块文件(模块名.py)
sys.path的初始值来自于以下地方:
包含脚本当前的路径,当前路径
PYTHONPATH
默认安装路径
sys.path初始化完成之后可以更改

编译过的Python文件: .pyc文件

built-in 模块
上面的例子中,当client被导入后,python解释器就在当前目录下寻找client.py的文件,然后再从环境变量PYTHONPATH寻找,如果这环境变量没有设定,也不要紧,解释器还会在安装预先设定的的一些目录寻找。这就是在导入下面这些标准模块,一切美好事情能发生的原因。

这些搜索目录可在运行时动态改变,比如将module1.py不放在当前目录,而放在一个冷僻的角落里。这里你就需要通过某种途径,如sys.path,来告知Python了。sys.path返回的是模块搜索列表,通过前后的输出对比和代码,应能理悟到如何增加新路径的方法了吧。非常简单,就是使用list的append()或insert()增加新的目录。

#module2.py
import sys
import os

print sys.path
workpath = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.insert(0, os.path.join(workpath, 'modules'))
print (sys.path)

其他的要点
模块能像包含函数定义一样,可包含一些可执行语句。这些可执行语句通常用来进行模块的初始化工作。这些语句只在模块第一次被导入时被执行。这非常重要,有些人以为这些语句会多次导入多次执行,其实不然。

模块在被导入执行时,python解释器为加快程序的启动速度,会在与模块文件同一目录下生成.pyc文件。我们知道python是解释性的脚本语言,而.pyc是经过编译后的字节码,这一工作会自动完成,而无需程序员手动执行。

3. 配置模块的初始化操作

  在了解了 __init__.py 的工作原理后,应该能理解该文件就是一个正常的python代码文件。

  因此可以将初始化代码放入该文件中。

  

 

 

  

 

  

类的概念在许多语言中出现,很容易理解。它将数据和操作进行封装,以便将来的复用。

2. 简化模块导入操作

  假设我们的模块包的目录结构如下:

.
└── mypackage
    ├── subpackage_1
    │   ├── test11.py
    │   └── test12.py
    ├── subpackage_2
    │   ├── test21.py
    │   └── test22.py
    └── subpackage_3
        ├── test31.py
        └── test32.py

   

  如果我们使用最直接的导入方式,将整个文件拷贝到工程目录下,然后直接导入:

from mypackage.subpackage_1 import test11
from mypackage.subpackage_1 import test12
from mypackage.subpackage_2 import test21
from mypackage.subpackage_2 import test22
from mypackage.subpackage_3 import test31
from mypackage.subpackage_3 import test32

  当然这个例子里面文件比较少,如果模块比较大,目录比较深的话,可能自己都记不清该如何导入。(很有可能,哪怕只想导入一个模块都要在目录中找很久)

  这种情况下,__init__.py 就很有作用了。我们先来看看该文件是如何工作的。

通常包总是一个目录,可以使用import导入包,或者from import来导入包中的部分模块。包目录下为首的一个文件便是 init.py。然后是一些模块文件和子目录,假如子目录中也有 init.py 那么它就是这个包的子包了。

在创建许许多多模块后,我们可能希望将某些功能相近的文件组织在同一文件夹下,这里就需要运用包的概念了。包对应于文件夹,使用包的方式跟模块也类似,唯一需要注意的是,当文件夹当作包使用时,文件夹需要包含__init__.py文件,主要是为了避免将文件夹名当作普通的字符串。init.py的内容可以为空,一般用来进行包的某些初始化工作或者设置__all__值,__all__是在from package-name import *这语句使用的,全部导出定义过的模块。

可以从包中导入单独的模块。
1). import PackageA.SubPackageA.ModuleA,使用时必须用全路径名
2). 变种: from PackageA.SubPackageA import ModuleA, 可以直接使用模块名而不用加上包前缀。
3). 也可以直接导入模块中的函数或变量:from PackageA.SubPackageA.ModuleA import functionA

import语句语法:

  1. 当使用from package import item时,item可以是package的子模块或子包,或是其他的定义在包中的名字(比如一个函数、类或变量)
    首先检查item是否定义在包中,不过没找到,就认为item是一个模块并尝试加载它,失败时会抛出一个ImportError异常。
  2. 当使用import item.subitem.subsubitem语法时,最后一个item之前的item必须是包,最后一个item可以是一个模块或包,但不能是类、函数和变量

  3. from pacakge import *
    如果包的__init__.py定义了一个名为__all__的列表变量,它包含的模块名字的列表将作为被导入的模块列表。
    如果没有定义__all__, 这条语句不会导入所有的package的子模块,它只保证包package被导入,然后导入定义在包中的所有名字。

python包是:
包是一个有层次的文件目录结构,它定义了由n个模块或n个子包组成的python应用程序执行环境。
通俗一点:包是一个包含__init__.py 文件的目录,该目录下一定得有这个__init__.py文件和其它模块或子包。

常见问题:
引入某一特定路径下的模块

使用sys.path.append(yourmodulepath)
将一个路径加入到python系统路径下,避免每次通过代码指定路径

利用系统环境变量 export PYTHONPATH=$PYTHONPATH:yourmodulepath,
直接将这个路径链接到类似/Library/Python/2.7/site-packages目录下
好的建议

经常使用if name == 'main',保证你写包既可以import又可以独立运行,用于test。
多次import不会多次执行模块,只会执行一次。可以使用reload来强制运行模块,但不提倡。
包(package)
为了组织好模块,将多个模块分为一个包。包是python模块文件所在的目录,且该目录下必须存在__init__.py文件。常见的包结构如下:

package_a
├── init.py
├── module_a1.py
└── module_a2.py
package_b
├── init.py
├── module_b1.py
└── module_b2.py
main.py
如果main.py想要引用packagea中的模块modulea1,可以使用:
from package_a import module_a1
import package_a.module_a1
如果packagea中的modulea1需要引用packageb,那么默认情况下,python是找不到packageb。我们可以使用sys.path.append('../'),可以在packagea中的__init__.py添加这句话,然后该包下得所有module都添加* import __init_即可。

2.1 __init__.py 是怎么工作的?

  实际上,如果目录中包含了 __init__.py 时,当用 import 导入该目录时,会执行 __init__.py 里面的代码。

  我们在mypackage目录下增加一个 __init__.py 文件来做一个实验:

.
└── mypackage
    ├── __init__.py
    ├── subpackage_1
    │   ├── test11.py
    │   └── test12.py
    ├── subpackage_2
    │   ├── test21.py
    │   └── test22.py
    └── subpackage_3
        ├── test31.py
        └── test32.py

  mypackage/__init__.py 里面加一个print,如果执行了该文件就会输出:

print("You have imported mypackage")

  下面直接用交互模式进行 import

>>> import mypackage
You have imported mypackage

  很显然,__init__.py 在包被导入时会被执行。

2.3  偷懒的导入方法

  有时候我们在做导入时会偷懒,将包中的所有内容导入

from mypackage import *

  这是怎么实现的呢? __all__ 变量就是干这个工作的。

  __all__ 关联了一个模块列表,当执行 from xx import * 时,就会导入列表中的模块。我们将 __init__.py 修改为 。

__all__ = ['subpackage_1', 'subpackage_2']

  这里没有包含 subpackage_3,是为了证明 __all__ 起作用了,而不是导入了所有子目录。

>>> from mypackage import *
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'subpackage_1', 'subpackage_2']
>>> 
>>> dir(subpackage_1)
['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__']

  子目录的中的模块没有导入!!!

  该例子中的导入等价于

from mypackage import subpackage_1, subpackage_2

  因此,导入操作会继续查找 subpackage_1 和 subpackage_2 中的 __init__.py 并执行。(但是此时不会执行 import *

  我们在 subpackage_1 下添加 __init__.py 文件:

__all__ = ['test11', 'test12']

# 默认只导入test11
from mypackage.subpackage_1 import test11

  再来导入试试

>>> from mypackage import *
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'subpackage_1', 'subpackage_2']
>>> 
>>> dir(subpackage_1)
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'test11']

  如果想要导入子包的所有模块,则需要更精确指定。

>>> from mypackage.subpackage_1 import *
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'test11', 'test12']

 

  我们经常在python的模块目录中会看到 "__init__.py"  这个文件,那么它到底有什么作用呢?

2.2  控制模块导入

  我们再做一个实验,在 mypackage/__init__.py 添加以下语句:

from subpackage_1 import test11

  我们导入 mypackage 试试:

>>> import mypackage
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/taopeng/Workspace/Test/mypackage/__init__.py", line 2, in <module>
    from subpackage_1 import test11
ImportError: No module named 'subpackage_1'

  报错了。。。怎么回事?

  原来,在我们执行import时,当前目录是不会变的(就算是执行子目录的文件),还是需要完整的包名。

from mypackage.subpackage_1 import test11

  综上,我们可以在__init__.py 指定默认需要导入的模块  

本文由时时app平台注册网站发布于编程知识,转载请注明出处:Python杂谈: __init__.py的作用【时时app平台注册网站

关键词: