python怎么导入包-Python模块导入与包构建最佳实践

[TOC]

最开始写程序的时候,都是一个文件里输入几行源码(python 的一个 web 框架bottle就特别强调自己是单文件框架)。随着程程式变大变复杂,一个文件很难承载如此多的功能,因此将代码拆分到不同的文件里,以模块(module)或者包(package)形式组织。既方便管理,也利于复用。python的模块和包非常简单,一个文件即模块,一个文件夹即一个包。

文件夹包必须在文件夹内声明一个init.py文件,包被导入的时候,默认会先执行这个文件的代码

导入方式

有了包机制,既可以把项目拆分封装。也可以实现独立的功能给别的项目使用。Python提供了 import 指令,鉴于历史原因,import 有相对导入(implicit/explicit relative import)和绝对导入(absolute import)两种方式。相对导入有隐式的导入(implicit)和显示导入(explicit)。

模块的导入都是相对于包而言。 import 指令是加载包的模块,通过from import 语句不仅可以加载模块,也可以导入 python 对象(类,函数,变量等)。模块最小组织单位是文件,import 后面的如果是文件则被认为是模块,若是文件里的 python 对象,则加载的不是模块。

模块搜索

Python的模块搜索大致有三种步骤。

首先搜索 sys.modules:这是一个列表,她存储了之前导入的所有模块,新导入的模块也会追加到这个列表里。若这里搜索不到,那么就会进行第二步。

其次搜索built-in module:Python 的标准库和安装的第三方软件包。如果还搜索不到模块,就进行最后一步。

最后搜索sys.path:被执行的python文件,其所在的目录会被追加到 sys.path 列表,也就是相对于被执行的文件的目录文件夹和系统在sys.path的也会被搜索。若任然找不到,最终会抛出一个ModuleNotFoundError错误。

Python文件加载方式

Python的文件加载方式有两种,直接运行和以模块方式加载。

以top-level方式直接加载:python + 文件名。如 python filename.py,或者 python dir/filename.py ,这样的python文件是作为 top-level 脚本运行,脚本文件的__name__属性会被设置成 __main__,同时其__package__属性设置为None。因为此时的文件作为顶层模块,它不属于任何一个包。直接运行脚本,会把脚本所在的目录追加到sys.path 之中。

以模块方式加载,使用-m解释参数,然后跟着文件路径,其中/替换成.。如 python -m filename 或者 python dir.filename,filename.py 变成了一个模块,其模块名为所在python执行目录下的包.模块.文件名。例如他的模块名是 filename或者 dir.filename。

这种方式,不会把脚本所在位置加载 sys.path 之中,而是会把执行 python 命令所在的目录加载 sys.path 中。但是被执行的脚本肯定也在执行命令所在文件或者子文件中,因此效果类似自身也属于 sys.path 之中。

自2.6以后,python 的模块名可以用下面的方式打印:

module_name = '"{}.{}".format(__package__, __name__) if __package is not None else __name__

相对导入和绝对导入

上文介绍了 python 脚本加载和模块搜索的基本方式。基于此,python提供了以相对或绝对导入两种包、模块的import方式。因python2和3分裂的历史,2默认是相对导入,3则是绝对导入,并且日常开发也推荐使用绝对导入方式。

那么它们两种有什么区别呢?

导入都是针对包而言

绝对导入:文件的 import 或者 from import 导入语句,都是从包的根路径开始

相对导入:导入的起始模块未必是从包路径开始,使用.或者.. 的方式是显示的相对导入,否则是隐式的

python3针对隐式相对导入会直接抛错。

Case Study

文字都过于抽象,下面针对code进行演示解析。文件目录结构如下,myproj 项目中,有一个pkg的包,包里有两个文件夹subpkg_a和subpkg_b两个子包,子包分别有几个py模块文件。

➜ myproj tree

.

└── pkg

├── __init__.py

├── main.py

├── subpkg_a

│ ├── __init__.py

│ ├── hello.py

│ └── world.py

└── subpkg_b

├── __init__.py

└── welcome.py

首先分析subpka_a包,即相对 subpkg_a 来分析 hello.py 和 world.py 直接的导入方式。暂时可以忽略其他文件或文件夹。

下面是几个文件的源码

➜ pkg cat subpkg_a/__init__.py subpkg_a/hello.py subpkg_a/world.py

#!/usr/bin/env python

# -*- coding:utf-8 -*-

if __package__:

print('subpkg_a.__init__.py: ', '{}.{}'.format(__package__, __name__))

else:

print('subpkg_a.__init__.py: ', '{}'.format(__name__))

------------------------

#!/usr/bin/env python

# -*- coding:utf-8 -*-

if __package__:

print('hello.py: ', '{}.{}'.format(__package__, __name__))

else:

print('hello.py: ', '{}'.format(__name__))

import world

------------------------

#!/usr/bin/env python

# -*- coding:utf-8 -*-

if __package__:

print('world.py: ', '{}.{}'.format(__package__, __name__))

else:

print('world.py: ', '{}'.format(__name__))

def say_world():

return 'world'

if __name__ == '__main__':

print(say_world())

hello.py 文件直接导入了world模块,然后运行结果如下:

➜ pkg python subpkg_a/hello.py

('hello.py: ', '__main__')

('world.py: ', 'world')

可以看见,hello.py 的模块名为 __main__ 。作为 top-level 执行的文件,其__package__是None, __name__是 __main__。 对于world.py 文件,因为它是被加载的模块,__name__就是文件名。

hello.py很好理解,作为top-level脚本执行,其本身不属于任何一个包。world.py是作为模块被加载的,但是hello.py 里也没指定从哪个包里加载。因此加载时候就按照模块搜索方式。因为hello.py执行的目录被加入了sys.path,world.py 与 hello.py 同级,因此自然能被搜索并成为模块。

隐式相对导入

由于上面的导入方式,模块都不属于任何一个包,自然就没有相对于绝对导入的说法。删掉subpka_a/__init__.py 文件也不会有影响。正如前面所介绍,python脚本若不是top-level,才有包概念的。修改执行方式如下:

➜ pkg python -m subpkg_a.hello

('subpkg_a.__init__.py: ', 'subpkg_a')

('hello.py: ', 'subpkg_a.__main__')

('world.py: ', 'subpkg_a.world')

可以看到,subpak_a 包的__init__.py 文件也被加载执行了,这表示包 subpak_a 被导入了。-m 的语法告诉了解释器,把当前执行命令的目录加入到 sys.path。以模块的方式加载 hello.py 文件,并且指定了hello 模块的父级是 subpkg_a,同理,处于同级的 world.py 文件也被隐式的包含在 subkag_a 包里,它的模块名subpkg_a.world。

上面的 import 语句中没有出现包 subpkg_a,所以是一种相对导入,也没有使用.或者.. 符号,所以是隐式的导入。隐式导入在python3下不支持,会抛错:

➜ pkg python3 -m subpkg_a.hello

subpkg_a.__init__.py: subpkg_a.subpkg_a

hello.py: subpkg_a.__main__

Traceback (most recent call last):

File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/runpy.py", line 193, in _run_module_as_main

"__main__", mod_spec)

File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/runpy.py", line 85, in _run_code

exec(code, run_globals)

File "/Users/master/myproj/pkg/subpkg_a/hello.py", line 9, in

import world

ModuleNotFoundError: No module named 'world'

显示相对导入

subpak_a 包的层级很清楚,因此改为显示相对导入也很简单,即:

➜ pkg cat subpkg_a/hello.py

#!/usr/bin/env python

# -*- coding:utf-8 -*-

if __package__:

print('hello.py: ', '{}.{}'.format(__package__, __name__))

else:

print('hello.py: ', '{}'.format(__name__))

from . import world

运行结果也正确:

➜ pkg python -m subpkg_a.hello

('subpkg_a.__init__.py: ', 'subpkg_a')

('hello.py: ', 'subpkg_a.__main__')

('world.py: ', 'subpkg_a.world')

使用了python -m subpkg_a.hello 命令执行,hello.py 和 world.py 分别属于 subpkg_a 包,因此 hello.py 里的 . 表示在包 subpkg_a 内,相对于__main__模块的导入同级模块。即subpkg_a.world,.即subpkg_a.。因此不会报错。

需要注意,显示的相对导入只有以模块加载的方式才能使用,否则会抛 Attempted relative import in non-package的错误

➜ pkg python subpkg_a/hello.py

('hello.py: ', '__main__')

Traceback (most recent call last):

File "subpkg_a/hello.py", line 9, in

from . import world

ValueError: Attempted relative import in non-package

正如前文所述,以top-level 的运行hello.py文件,hello.py的模块名是__main__, world.py 的模块名是world,两者不属于任何一个包,自然也没有模块的层级。. 是指相对与包下面的模块的路径进行导入。正如这样没有包概念,因此抛错。

对于subpak_b包里的模块,需要使用..操作符,修改hello.py 如下:

...

from . import world

from ..subpkg_b import welcome

需要注意的是,python -m subpkg_a.hello的执行方式,最顶级的包是 subpkg_a,而subpkg_b 是搜索不到的,需要更上层的目录来执行:

➜ pkg python -m subpkg_a.hello

('subpkg_a.__init__.py: ', 'subpkg_a')

('hello.py: ', 'subpkg_a.__main__')

('world.py: ', 'subpkg_a.world')

Traceback (most recent call last):

File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/runpy.py", line 162, in _run_module_as_main

"__main__", fname, loader, pkg_name)

File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/runpy.py", line 72, in _run_code

exec code in run_globals

File "/Users/master/myproj/pkg/subpkg_a/hello.py", line 10, in

from ..subpkg_b import welcome

ValueError: Attempted relative import beyond toplevel package

➜ pkg cd ../

➜ myproj python -m pkg.subpkg_a.hello

('subpkg_a.__init__.py: ', 'pkg.subpkg_a')

('hello.py: ', 'pkg.subpkg_a.__main__')

('world.py: ', 'pkg.subpkg_a.world')

('welcome.py: ', 'pkg.subpkg_b.welcome')

综上所述,相对导入,导入的路径中,都没有出现包名。

混用隐式和显示

通常对于subpak_a 和subpak_b,它们自身实现逻辑可以使用显示或者隐式导入。对于它的调用者,pkg下的main.py 可以直接引用这两个包。此时的参考包是pkg。

if __package__:

print('main.py: ', '{}.{}'.format(__package__, __name__))

else:

print('main.py: ', '{}'.format(__name__))

from subpkg_a import hello

在main中隐式相对导入了subpak_a 和 hello 模块

➜ myproj python pkg/main.py

('main.py: ', '__main__')

('welcome.py: ', 'subpkg_b.welcome')

('subpkg_a.__init__.py: ', 'subpkg_a')

('hello.py: ', 'subpkg_a.hello')

('world.py: ', 'subpkg_a.world')

Traceback (most recent call last):

File "pkg/main.py", line 12, in

from subpkg_a import hello

File "/Users/master/myproj/pkg/subpkg_a/hello.py", line 11, in

from ..subpkg_b import welcome

ValueError: Attempted relative import beyond toplevel package

from subpkg_b import welcome语句正常执行了,from subpkg_a import hello也正常,这符合前面的说明。

此时main是top-level,它不属于任何一个包,但是subpkg_a,subpkg_b 也不属于任何一个包,但是它本身是一个包,所以导入它是没问题,并且它里面的hello和world导入也正常。

执行到 from ..subpkg_b import welcome 语句的时候报错了。正如前面的结果,subpkg_a 和 subpkg_b 是同级,可是在 subpkg_a 包并不知道 subpkg_b 包的存在,因此需要把他们共有的包 pkg 引入到包层级中,即

➜ myproj python -m pkg.main

('main.py: ', 'pkg.__main__')

('welcome.py: ', 'pkg.subpkg_b.welcome')

('subpkg_a.__init__.py: ', 'pkg.subpkg_a')

('hello.py: ', 'pkg.subpkg_a.hello')

('world.py: ', 'pkg.subpkg_a.world')

鉴于 welcome 已经被导入过,因此 hello.py 将不会再导入 welcome 模块。

绝对导入

隐式相对导入在py3被禁止了,显式相对导入也不是默认,那么最好还是使用绝对导入。即从包的起始位置书写import路径。

修改main.py,将import从跟包开始书写路径

➜ myproj cat pkg/main.py

if __package__:

print('main.py: ', '{}.{}'.format(__package__, __name__))

else:

print('main.py: ', '{}'.format(__name__))

from pkg.subpkg_b import welcome

from pkg.subpkg_a import hello

➜ myproj python pkg/main.py

('main.py: ', '__main__')

Traceback (most recent call last):

File "pkg/main.py", line 11, in

from pkg.subpkg_b import welcome

ImportError: No module named pkg.subpkg_b

可以看到,与上次执行不一样,from pkg.subpkg_b import welcome 这一句就报错了。当前的 sys.path 是 main.py 所在的目录,并不包括 pkg 所在的目录,因此搜索包的时候,搜索不到 pkg。解决方案也有两种。

因为 sys.path 没有,那么加上即可。在main.py 中加上

➜ myproj cat pkg/main.py

if __package__:

print('main.py: ', '{}.{}'.format(__package__, __name__))

else:

print('main.py: ', '{}'.format(__name__))

import sys

sys.path.append('./')

from pkg.subpkg_b import welcome

from pkg.subpkg_a import hello

➜ myproj python pkg/main.py

('main.py: ', '__main__')

('welcome.py: ', 'pkg.subpkg_b.welcome')

('subpkg_a.__init__.py: ', 'pkg.subpkg_a')

('hello.py: ', 'pkg.subpkg_a.hello')

('world.py: ', 'pkg.subpkg_a.world')

尽管针对 sys.path 进行 hack 可以实现绝对导入,可是这种方式始终一点也不 make sence。正如前面解决方式一样,可以使用 -m 以模块方式加载。毕竟 -m 可以把当前执行路径加入到sys.path中,去掉 sys.path.append的语句,再运行:

➜ myproj python -m pkg.main

('main.py: ', 'pkg.__main__')

('welcome.py: ', 'pkg.subpkg_b.welcome')

('subpkg_a.__init__.py: ', 'pkg.subpkg_a')

('hello.py: ', 'pkg.subpkg_a.hello')

('world.py: ', 'pkg.subpkg_a.world')

使用 -m 方式使用一个包,在python也是挺常见的,例如开启一个服务器和格式化json字符串

➜ myproj python3 -m http.server

Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

➜ myproj echo '[{"hello": "world"}, {"python": "life is short"}]' | python -m json.tool

[

{

"hello": "world"

},

{

"python": "life is short"

}

]

然而,实际生产中,写一个package或者lib,更多是被 install 后再导入运行。install 保证了它将会处在sys.path,被导入等同于以 -m 方式被加载执行。因此不太会有 sys.path 和ModuleNotFoundError问题。例如将hello改为

from subpkg_a import world

变成绝对导入之后,直接运行会报错:

➜ pkg python subpkg_a/hello.py

('hello.py: ', '__main__')

Traceback (most recent call last):

File "subpkg_a/hello.py", line 10, in

from subpkg_a import world

ImportError: No module named subpkg_a

main.py 改为

from subpkg_a import hello

模拟subpkg_a作为一个独立的lib,其本身使用绝对导入,然后pkg里的main使用这个包,直接运行,并没有报错

➜ myproj python pkg/main.py

('main.py: ', '__main__')

('subpkg_a.__init__.py: ', 'subpkg_a')

('hello.py: ', 'subpkg_a.hello')

('world.py: ', 'subpkg_a.world')

➜ myproj

因此使用绝对导入开发一个 lib 是更好的实践。可是正如上面 subpkg_a 所面临的问题,开发过程中,直接运行,可能会报错,不得不使用 -m 的方式。为了更好的开发,可以使用下面介绍的包结构。

Python Lib 构建推荐

带有__init__.py 文件夹即成为一个包,包,模块相互组织起来即成为lib。先看一个相对导入,即构建包的时候。

➜ demo tree mylib

mylib

├── mylib

│ ├── __init__.py

│ ├── greet

│ │ ├── __init__.py

│ │ ├── hello.py

│ │ └── world.py

│ └── main.py

└── setup.py

2 directories, 6 files

其中 hello.py world.py 和 main.py 的内容如下:

➜ mylib cat greet/hello.py greet/world.py main.py

#!/usr/bin/env python

# -*- coding:utf-8 -*-

from . import world

def say_hello():

return 'hello ' + world.say_world()

if __name__ == '__main__':

print(say_hello())

#!/usr/bin/env python

# -*- coding:utf-8 -*-

def say_world():

return 'world'

if __name__ == '__main__':

print(say_world())

#!/usr/bin/env python

# -*- coding:utf-8 -*-

from .greet import hello

def do_greet():

return hello.say_hello()

if __name__ == '__main__':

print(do_greet())

运行 python main.py 也能正常运行

然后使用 setup打包进行安装。

➜ mylib cat setup.py

#!/usr/bin/env python

# -*- coding:utf-8 -*-

from setuptools import setup, find_packages

setup(

name='mylib',

version='1.0.0',

decription='simple lib demo',

long_description='README.md',

author='jiamin',

author_email='maojiamin@daixm.com',

licens='',

packages=find_packages(exclude=('tests', 'docs')),

test_suite='tests'

)

执行 python setup.py bdist_wheel,会在 lib 下的 dist 文件夹生成一个 mylib-1.0.0-py2-none-any.whl 包。使用 pip 可以直接安装

(venv) ➜ myproj pip list

Package Version

---------- -------

pip 19.0.1

setuptools 40.6.3

wheel 0.32.3

(venv) ➜ myproj pip install ~/mylib/dist/mylib-1.0.0-py2-none-any.whl

DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7.

Processing /Users/master/mylib/dist/mylib-1.0.0-py2-none-any.whl

Installing collected packages: mylib

Successfully installed mylib-1.0.0

(venv) ➜ myproj pip list

Package Version

---------- -------

mylib 1.0.0

pip 19.0.1

setuptools 40.6.3

wheel 0.32.3

(venv) ➜ myproj ipython

In [1]: import mylib

In [2]: from mylib.main import do_greet

In [3]: do_greet()

Out[3]: 'hello world'

In [4]:

使用相对导入也可以构建一个包。

更好的python包构建方式

使用显示相对导入包构建方式,一个好处就是,报名修改了,也会不用修改包内模块的导入语句。而绝对导入包含了包名。但是绝对导入对于本地包的处理,有更好的方式,因此也是python3的默认方式。

构建一个python lib。和包结构和相对导入类似,下面增加更多的应用场景。项目结构目录如下

➜ mylib tree

.

├── mylib

│ ├── __init__.py

│ ├── cron

│ │ ├── __init__.py

│ │ └── tasks.py

│ ├── greet

│ │ ├── __init__.py

│ │ ├── hello.py

│ │ └── world.py

│ └── main.py

├── setup.py

├── docs

├── README.md

└── tests

├── __init__.py

├── __init__.pyc

└── test_greet.py

4 directories, 11 files

增加了tests目录和docs目录以及README.md。几个文件代码如下,所有导入都使用绝对导入,即从 mylib开始导入

➜ mylib cat mylib/greet/hello.py

from mylib.greet import world

...

➜ mylib cat mylib/cron/tasks.py

#!/usr/bin/env python

# -*- coding:utf-8 -*-

from mylib.greet import hello

def do_task():

return 'do task : ' + hello.say_hello()

if __name__ == '__main__':

print(do_task())

➜ mylib cat tests/test_greet.py

#!/usr/bin/env python

# -*- coding:utf-8 -*-

import unittest

from mylib.greet import world

from mylib.greet import hello

class TestGreet(unittest.TestCase):

def test_say_world(self):

self.assertEqual('world', world.say_world())

def test_say_hello(self):

self.assertEqual('hello world', hello.say_hello())

if __name__ == '__main__':

unittest.main()

在mylib内,若想要执行 corn下面的task,必须以 -m 方式运行,否则会抛错

➜ mylib python mylib/cron/tasks.py

Traceback (most recent call last):

File "mylib/cron/tasks.py", line 4, in

from mylib.greet import hello

ImportError: No module named mylib.greet

➜ mylib python -m mylib.cron.tasks

do task : hello world

同样的,执行 tests 也是需要制定 -m。

hydra 里的cron,都是使用 sys.path.append方式,将执行脚本追加到path。使用 -m 方式会比hack sys.path 更好

运行测试

➜ mylib python -m tests.test_greet

..

----------------------------------------------------------------------

Ran 2 tests in 0.000s

OK

测试 测试

但是,其实setup工具,提供了测试方法,并且setup里还可以指定不同的测试方式,即声明 test_suite='tests'

➜ mylib python setup.py test

running test

running egg_info

creating mylib.egg-info

writing mylib.egg-info/PKG-INFO

writing top-level names to mylib.egg-info/top_level.txt

writing dependency_links to mylib.egg-info/dependency_links.txt

writing manifest file 'mylib.egg-info/SOURCES.txt'

reading manifest file 'mylib.egg-info/SOURCES.txt'

writing manifest file 'mylib.egg-info/SOURCES.txt'

running build_ext

test_say_hello (tests.test_greet.TestGreet) ... ok

test_say_world (tests.test_greet.TestGreet) ... ok

----------------------------------------------------------------------

Ran 2 tests in 0.000s

当运行了上面的测试之后,会发现当前目录多了一个文件夹mylib.egg-info

➜ mylib ls

mylib mylib.egg-info setup.py tests

安装项目

Lib开发完之后,自然是可以进行打包然后分发后install。然而测试开发的时候,如果执行脚本,都需要加上 -m,整个开发过程还是蛮繁琐,因此python的setup.py 提供了一个develop参数,进行了一次mock安装。

(venv) ➜ mylib pip list

Package Version

---------- -------

pip 19.0.1

setuptools 40.6.3

wheel 0.32.3

(venv) ➜ mylib python setup.py develop

running develop

running egg_info

writing mylib.egg-info/PKG-INFO

writing top-level names to mylib.egg-info/top_level.txt

writing dependency_links to mylib.egg-info/dependency_links.txt

reading manifest file 'mylib.egg-info/SOURCES.txt'

writing manifest file 'mylib.egg-info/SOURCES.txt'

running build_ext

Creating /Users/master/myproj/venv/lib/python2.7/site-packages/mylib.egg-link (link to .)

Adding mylib 1.0.0 to easy-install.pth file

Installed /Users/master/myproj/mylib

Processing dependencies for mylib==1.0.0

Finished processing dependencies for mylib==1.0.0

(venv) ➜ mylib pip list

Package Version Location

---------- ------- --------------------------

mylib 1.0.0 /Users/master/myproj/mylib

pip 19.0.1

setuptools 40.6.3

wheel 0.32.3

(venv) ➜ mylib python setup.py develop

running develop

running egg_info

writing mylib.egg-info/PKG-INFO

writing top-level names to mylib.egg-info/top_level.txt

writing dependency_links to mylib.egg-info/dependency_links.txt

reading manifest file 'mylib.egg-info/SOURCES.txt'

writing manifest file 'mylib.egg-info/SOURCES.txt'

running build_ext

Creating /Users/master/myproj/venv/lib/python2.7/site-packages/mylib.egg-link (link to .)

mylib 1.0.0 is already the active version in easy-install.pth

Installed /Users/master/myproj/mylib

Processing dependencies for mylib==1.0.0

Finished processing dependencies for mylib==1.0.0

(venv) ➜ mylib python mylib/cron/tasks.py

do task : hello world

由此可见,执行了 python setup.py develop, 会在环境的site-package创建一个 mylib.egg-link文件,这个文件的内容是 /Users/master/myproj/mylib,即指向当前开发环境包目录,因此等价于安装了包到环境中。自然可以通过pip list 查看。也就是可以直接使用脚本方式运行,不再需要 -m了,并且也能再开发的时候,进行针对安装以后的行为效果进行调试。

总结

程序规模变大变复杂,通常进行模块拆分和封包复用。python文件及模块的基本组织单位,文件夹则是基础包。包或者模块的引用可以使用 import 或者 from import 语法。

Import有相对导入和绝对导入,相对导入又有显式和隐式两种。显式则使用.或者..操作符。相对还是绝对,针对的是python文件被加载的方式。

直接运行python文件则是以top-level方式,当前文件模块名是__main__,它本身就是顶级模块,不存在包的概念。若使用-m参数,则以模块方式加载,模块方式加载都是相对包而言。. 表示在同一个包内,被相对被加载文件的路径进行加载导入的模块。

相对导入的文件里不会出现包名,绝对导入的文件里,import语句必须包含包名。同时所导入的包都必须从包名的根路径开始,写出完整的模块路径。

Python3不在支持隐式相对导入。官方也更推荐使用绝对导入。因此介绍了使用绝对导入构建一个lib,所使用的方式包括项目源码,文档,测试等,这也是facebook 的 tornado 的方式。其中使用 python setup.py test 进行单元测试。以及使用 pyton setup.py develop和mock安装,使得开发调试更方便。

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值