SlideShare a Scribd company logo
1 of 43
Download to read offline
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Python Module 引⼊入机
制与最佳实践
—刘畅
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
演讲内容
1. 为什么会发⽣生ImportError?
2. 如何避免 sys.path.append 的hack⾏行为?
3. python -m 是怎么回事?
4. python项⺫⽬目的⺫⽬目录结构如何组织?
5. import 语句是如何实现的?
6. 如何定制化⾃自⼰己的import?
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Python的包引⼊入机制
• 





import string

print string.a
a = 2
pkg/

pkg/__init__.py

pkg/main.py
pkg/string.py
• 如果运⾏行 pkg/main.py ⽂文件的话,会发⽣生什么事情?
• import的string 是标准库的string还是当前⺫⽬目录的string呢?
• 如果明确的指明⾃自⼰己要引⼊入的module?
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Python的包引⼊入机制
• 相对引⽤用 implicit/explicit relative import
• 绝对引⽤用 absolute import
• 推荐阅读:https://docs.python.org/2/whatsnew/
2.5.html#pep-328-absolute-and-relative-imports
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
relative_import
代码⽰示例:
from .foo import bar

from ..foo import bar
• 在 py2.7 之前是python的默认的module引⼊入⽅方式
• 推荐阅读:PEP0328
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
absolute Import
代码⽰示例:
from __future__ import absolute_import
from API.foo import bar
from API.API2.foo import bar
• 在py2.5实现,需要明确的声明从 __future__ 中引⼊入该机制。在py2.7和
py3中成为默认的引⼊入机制。
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
相对引⽤用!出错!
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
绝对引⽤用!出错!
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
如何解决引⽤用⽤用出错的问题?
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
import sys
sys.path.append(‘..’)
不推荐!
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
⼩小知识:加载python的⼆二
种⽅方式
• as the top-level script

python foo/myfile.py
• as a module:

python -m foo.myfile
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
⼆二种加载⽅方式的不同之
处
When a file is loaded, it is given a name (which is
stored in its __name__ attribute). If it was loaded as
the top-level script, its name is __main__. If it was
loaded as a module, its name is the filename,
preceded by the names of any packages/
subpackages of which it is a part, separated by dots.
• 推荐阅读:http://stackoverflow.com/questions/
14132789/pythonrelative-imports-for-the-billionth-
time
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
为什么这个例⼦子出错?
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
出错的原因
• 当使⽤用⽤用python subpackage1/foo.py时,foo.py 被当成了
top level script,它的__name__属性被设置为__main__。
• relative import则是根据__name__来确定被引⼊入⼊入包的位
置。
Relative imports use a module's __name__ attribute to
determine that module's position in the package hierarchy. If
the module’s name does not contain any package
information (e.g. it is set to '__main__') then relative imports
are resolved as if the module were a top level module,
regardless of where the module is actually located on the file
system.
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
解决办法
• 让运⾏行⾏行的⽂文⽂文件有完整的 __name__,也就是使⽤用⽤用 module 的
load⽅方⽅方式
• 推荐阅读:PEP 0366
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
⼩小知识
• python在执⾏行⾏行程序时,会⾃自⾃自动的把CWD添加到
sys.path中。
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
为什么这个例⼦子出错?
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
出错的原因
• 例⼦子从subpackage1.bar中引⼊入hello函数。但是
sys.path中找不到subpackage1这个包。
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
解决办法
• python -m subpackage1.foo2
• 将cwd修改为合适的⺫⽬目录
• 将subpackage1添加到
sys.path
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
我们应该使⽤用哪种引⽤用⽅方式?
pep8推荐absolute import
在特别复杂的包结构时,
也可以使⽤用明确的相对引⽤用
相对引⽤用(不带.号)在python3被移除
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
facebook/tornado 的代码⽰示例
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Python项⺫⽬目的推荐⺫⽬目录结构
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
• 每个项⺫⽬目⼀一个⺫⽬目录(不是python package,没有
__init__.py)
• 顶层⺫⽬目录下⾯面⼀一个同名(也可以不同名)的python
package。
• ⽂文档⺫⽬目录在顶层⺫⽬目录下
• 各种脚本,配置⽂文件,描述⽂文件在顶层⺫⽬目录下(好处?
想⼀一想sys.path的进制)
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
import 实现
> from package import module as mymodule
1. 查找相应的module
2. 加载module到local namespace
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
查找过程
1. 检查 sys.modules (保存了之前import的类库的缓存),如
果module被找到,则⾛走到第⼆二步。
2. 检查 sys.meta_path。meta_path 是⼀一个 list,⾥里⾯面保存着
⼀一些  finder对象,如果找到该module的话,就会返回⼀一个
finder对象。
3.检查⼀一些隐式的finder对象,不同的python实现有不同的隐
式finder,但是都会有 sys.path_hooks,
sys.path_importer_cache 以及 sys.path。
4.抛出 ImportError
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
finder 对象
• finder 对象⽤用来查找module的 loader对象
• finder 必须实现⼀一个叫作 find_module 的⽅方法,当
find_module成功,返回 loader,否则返回None
• 可以 python -m modulefinder filename.py,打印出
finder的具体信息
• 推荐阅读 https://docs.python.org/2/library/
modulefinder.html 和 PEP302
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
loader对象
loader对象要实现load_module⽅方法
load_module(module)⽅方法会执⾏行:
• 检查sys.module,如果有则返回,否则添加新
的。如果load失败,还要删除之前添加的module
object
• 设置__file__ __name__ __path__ __loader__
__package__
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
⼀一个meta_path的例⼦子
import sys
class Watcher(object):
@classmethod
def find_module(cls, name, path, target=None):
print('Importing', name, path, target)
return None
sys.meta_path.insert(0, Watcher)
import socket
Importing socket None None
Importing _socket None None
Importing enum None None
Importing collections None None
Importing _collections None None
Importing operator None None
Importing _operator None None
Importing keyword None None
Importing heapq None None
Importing itertools None None
Importing _heapq None None
Importing reprlib None None
Importing types None None
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
sys.path
• sys.path 由三个部分组成
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
sys.prefix
python 的安装⺫⽬目录
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
PYTHONPATH
环境变量,类似于PATH
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
site.py
• 定义了 site-package 的⺫⽬目录
• 可以python -S关闭site.py的加载
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
import hooks
• __import__:内建函数
• imputil:py2.6开始被声明废弃,py3彻底移除
• importlib:py3中添加,同时添加到py2.7
• 推荐阅读:https://docs.python.org/3.5/library/
importlib.html
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
import hook⽤用法
__import__ 的⽤用法,不推荐:
In [1]: spam = __import__('sys')

In [2]: spam

Out[2]: <module 'sys' (built-in)>
importlib 的⽤用法,推荐:
# Same as: import spam

In [1]: spam = importlib.import_module(‘spam')


# Same as: from . import spam

In [2]: spam = importlib.import_module('.spam',
__package__)
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Hook Example
>>> def my_import(modname *args, imp=__import__):
... print('importing', modname)
... return imp(modname, *args)
...
>>> import builtins
>>> builtins.__import__ = my_import
>>>
>>> import socket
importing socket
importing _socket
…
builtins 只⽀支持py3,上⾯面的例⼦子要在py3下运⾏行
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Hook Example2
# spam.py

from importlib.util import find_spec

if find_spec('foo'):

import foo

else:

import simplefoo
更加直观的⽅方式import包,出错信息更丰富
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Hook Example3
• Lazy Importer
• https://pypi.python.org/pypi/Importing
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Hook Example4
• ⾃自定义⼀一个python package proxy
• flask 的插件机制,当引⼊入flask.ext.api等包时⾃自动
import相应的包。
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
flask 实现
def load_module(self, fullname):
if fullname in sys.modules:
return sys.modules[fullname]
modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff]
for path in self.module_choices:
realname = path % modname
try:
__import__(realname)
except ImportError:
exc_type, exc_value, tb = sys.exc_info()
sys.modules.pop(fullname, None)
if self.is_important_traceback(realname, tb):
reraise(exc_type, exc_value, tb.tb_next)
continue
module = sys.modules[fullname] = sys.modules[realname]
if '.' not in modname:
setattr(sys.modules[self.wrapper_module], modname, module)
return module
raise ImportError('No module named %s' % fullname)
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Hook Example5
>>> import requests
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named ‘requests'
>>> import autoinstall
>>> import requests
Installing requests
>>> requests
<module 'requests' from '...python3.4/site-packages/requests/
__init__.py’>
在import第三⽅方包时,⾃自动安装相应的依赖
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
import sys
import subprocess
import importlib.util
class AutoInstall(object):
_loaded = set()
@classmethod
def find_spec(cls, name, path, target=None):
if path is None!and name not in! cls._loaded:
cls._loaded.add(name)
print("Installing",! name)
try:
out = subprocess.check_output(
[sys.executable, '-m', 'pip', 'install', name])
return importlib.util.find_spec(name)
except Exception as e:
print("Failed")
return None
sys.meta_path.append(AutoInstall)
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Refrences
• Modules and Packages:Live and Let Die! —David Beazley
• http://stackoverflow.com/questions/14132789/pythonrelative-imports-for-the-billionth-time
• https://github.com/tornadoweb/tornado
• https://github.com/mitsuhiko/flask
• PEP 0328
• PEP 0273
• PEP 0338
• PEP 0008
• PEP 0302
• PEP 0366
• PEP 0451
北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic!
Thanks
• 刘畅
• GeneDock Senior Software Engineer
• http://liuchang0812.com
• RocksDB/Spark/Tornado Contributor
• https://genedock.com/joinus/

More Related Content

Similar to Python的module机制与最佳实践

Py + gi 高效 c 庫整合方案
Py +  gi   高效 c 庫整合方案Py +  gi   高效 c 庫整合方案
Py + gi 高效 c 庫整合方案Leo Zhou
 
Python meetup 1
Python meetup 1Python meetup 1
Python meetup 1Vic Yang
 
Continuous Delivery with Ansible x GitLab CI
Continuous Delivery with Ansible x GitLab CIContinuous Delivery with Ansible x GitLab CI
Continuous Delivery with Ansible x GitLab CIChu-Siang Lai
 
Continuous Delivery with Ansible x GitLab CI (2e)
Continuous Delivery with Ansible x GitLab CI (2e)Continuous Delivery with Ansible x GitLab CI (2e)
Continuous Delivery with Ansible x GitLab CI (2e)Chu-Siang Lai
 
快快樂樂學 Scrapy
快快樂樂學 Scrapy快快樂樂學 Scrapy
快快樂樂學 Scrapyrecast203
 
Python安全编码
Python安全编码Python安全编码
Python安全编码Leo Zhou
 
該怎麼樣(認真的)部署你的 Python Web 應用程式?
該怎麼樣(認真的)部署你的 Python Web 應用程式?該怎麼樣(認真的)部署你的 Python Web 應用程式?
該怎麼樣(認真的)部署你的 Python Web 應用程式?Andy Dai
 
如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)
如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)
如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)Yanwei Liu
 
用Cython封装c++代码为python模块的一点经验
用Cython封装c++代码为python模块的一点经验用Cython封装c++代码为python模块的一点经验
用Cython封装c++代码为python模块的一点经验Leo Zhou
 
Python和web开发
Python和web开发Python和web开发
Python和web开发moonbingbing
 
100902 wm4wps-py-webdev
100902 wm4wps-py-webdev100902 wm4wps-py-webdev
100902 wm4wps-py-webdevZoom Quiet
 
Python 于 webgame 的应用
Python 于 webgame 的应用Python 于 webgame 的应用
Python 于 webgame 的应用勇浩 赖
 
DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代
DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代
DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代scott liao
 
使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)Max Lai
 
Py ladies 0928
Py ladies 0928Py ladies 0928
Py ladies 0928Yen_CY
 
Python与抓包
Python与抓包Python与抓包
Python与抓包Leo Zhou
 
Windows 環境下建構python 跑VMware API
Windows 環境下建構python 跑VMware API Windows 環境下建構python 跑VMware API
Windows 環境下建構python 跑VMware API 裝機安 Angelo
 
Continuous Delivery Workshop with Ansible x GitLab CI
Continuous Delivery Workshop with Ansible x GitLab CIContinuous Delivery Workshop with Ansible x GitLab CI
Continuous Delivery Workshop with Ansible x GitLab CIChu-Siang Lai
 

Similar to Python的module机制与最佳实践 (20)

Py + gi 高效 c 庫整合方案
Py +  gi   高效 c 庫整合方案Py +  gi   高效 c 庫整合方案
Py + gi 高效 c 庫整合方案
 
Python meetup 1
Python meetup 1Python meetup 1
Python meetup 1
 
Continuous Delivery with Ansible x GitLab CI
Continuous Delivery with Ansible x GitLab CIContinuous Delivery with Ansible x GitLab CI
Continuous Delivery with Ansible x GitLab CI
 
Continuous Delivery with Ansible x GitLab CI (2e)
Continuous Delivery with Ansible x GitLab CI (2e)Continuous Delivery with Ansible x GitLab CI (2e)
Continuous Delivery with Ansible x GitLab CI (2e)
 
快快樂樂學 Scrapy
快快樂樂學 Scrapy快快樂樂學 Scrapy
快快樂樂學 Scrapy
 
Python安全编码
Python安全编码Python安全编码
Python安全编码
 
該怎麼樣(認真的)部署你的 Python Web 應用程式?
該怎麼樣(認真的)部署你的 Python Web 應用程式?該怎麼樣(認真的)部署你的 Python Web 應用程式?
該怎麼樣(認真的)部署你的 Python Web 應用程式?
 
如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)
如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)
如何幫樹莓派安裝常用的Python套件(How to Install Python Package on Raspberry Pi)
 
用Cython封装c++代码为python模块的一点经验
用Cython封装c++代码为python模块的一点经验用Cython封装c++代码为python模块的一点经验
用Cython封装c++代码为python模块的一点经验
 
Python和web开发
Python和web开发Python和web开发
Python和web开发
 
100902 wm4wps-py-webdev
100902 wm4wps-py-webdev100902 wm4wps-py-webdev
100902 wm4wps-py-webdev
 
Python 于 webgame 的应用
Python 于 webgame 的应用Python 于 webgame 的应用
Python 于 webgame 的应用
 
DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代
DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代
DevOpsDays Taipei 2018 - Puppet 古早味、新感受:改造老牌企業進入自動化時代
 
使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)
 
Django step0
Django step0Django step0
Django step0
 
Py ladies 0928
Py ladies 0928Py ladies 0928
Py ladies 0928
 
Python与抓包
Python与抓包Python与抓包
Python与抓包
 
Windows 環境下建構python 跑VMware API
Windows 環境下建構python 跑VMware API Windows 環境下建構python 跑VMware API
Windows 環境下建構python 跑VMware API
 
Continuous Delivery Workshop with Ansible x GitLab CI
Continuous Delivery Workshop with Ansible x GitLab CIContinuous Delivery Workshop with Ansible x GitLab CI
Continuous Delivery Workshop with Ansible x GitLab CI
 
Python系列3
Python系列3Python系列3
Python系列3
 

More from Leo Zhou

第三名 3rd zhyict
第三名 3rd zhyict第三名 3rd zhyict
第三名 3rd zhyictLeo Zhou
 
异常检测在苏宁的实践
异常检测在苏宁的实践异常检测在苏宁的实践
异常检测在苏宁的实践Leo Zhou
 
第二名 2nd 火眼金睛
第二名 2nd 火眼金睛第二名 2nd 火眼金睛
第二名 2nd 火眼金睛Leo Zhou
 
第四名 4th H3C AI Institute
第四名 4th H3C AI Institute第四名 4th H3C AI Institute
第四名 4th H3C AI InstituteLeo Zhou
 
第一名 1st Bocoiops
第一名 1st Bocoiops第一名 1st Bocoiops
第一名 1st BocoiopsLeo Zhou
 
第六名 6th Aurora
第六名 6th Aurora第六名 6th Aurora
第六名 6th AuroraLeo Zhou
 
AI使能网络自动驾驶 AI Building Autonomous Driving Network
AI使能网络自动驾驶 AI Building Autonomous Driving NetworkAI使能网络自动驾驶 AI Building Autonomous Driving Network
AI使能网络自动驾驶 AI Building Autonomous Driving NetworkLeo Zhou
 
2.2 go在阿里云cdn系统的应用
2.2 go在阿里云cdn系统的应用2.2 go在阿里云cdn系统的应用
2.2 go在阿里云cdn系统的应用Leo Zhou
 
1.6 米嘉 gobuildweb
1.6 米嘉 gobuildweb1.6 米嘉 gobuildweb
1.6 米嘉 gobuildwebLeo Zhou
 
1.4 go在数据存储上面的应用—毛剑
1.4 go在数据存储上面的应用—毛剑1.4 go在数据存储上面的应用—毛剑
1.4 go在数据存储上面的应用—毛剑Leo Zhou
 
1.2 刘奇 go在分布式数据库中的应用
1.2 刘奇 go在分布式数据库中的应用1.2 刘奇 go在分布式数据库中的应用
1.2 刘奇 go在分布式数据库中的应用Leo Zhou
 
Protocol libraries the right way
Protocol libraries the right wayProtocol libraries the right way
Protocol libraries the right wayLeo Zhou
 
美团数据库运维平台介绍
美团数据库运维平台介绍美团数据库运维平台介绍
美团数据库运维平台介绍Leo Zhou
 
特卖场景下的大数据平台和机器学习实践
特卖场景下的大数据平台和机器学习实践特卖场景下的大数据平台和机器学习实践
特卖场景下的大数据平台和机器学习实践Leo Zhou
 
我的互联网运维理论与实践
我的互联网运维理论与实践我的互联网运维理论与实践
我的互联网运维理论与实践Leo Zhou
 
如何选择 Docker 监控方案
如何选择 Docker 监控方案如何选择 Docker 监控方案
如何选择 Docker 监控方案Leo Zhou
 
美团数据库运维平台介绍
美团数据库运维平台介绍美团数据库运维平台介绍
美团数据库运维平台介绍Leo Zhou
 
The net is dark and full of terrors - James Bennett
The net is dark and full of terrors - James BennettThe net is dark and full of terrors - James Bennett
The net is dark and full of terrors - James BennettLeo Zhou
 
Hypothesis randomised testing for django
Hypothesis randomised testing for djangoHypothesis randomised testing for django
Hypothesis randomised testing for djangoLeo Zhou
 
NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享
NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享
NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享Leo Zhou
 

More from Leo Zhou (20)

第三名 3rd zhyict
第三名 3rd zhyict第三名 3rd zhyict
第三名 3rd zhyict
 
异常检测在苏宁的实践
异常检测在苏宁的实践异常检测在苏宁的实践
异常检测在苏宁的实践
 
第二名 2nd 火眼金睛
第二名 2nd 火眼金睛第二名 2nd 火眼金睛
第二名 2nd 火眼金睛
 
第四名 4th H3C AI Institute
第四名 4th H3C AI Institute第四名 4th H3C AI Institute
第四名 4th H3C AI Institute
 
第一名 1st Bocoiops
第一名 1st Bocoiops第一名 1st Bocoiops
第一名 1st Bocoiops
 
第六名 6th Aurora
第六名 6th Aurora第六名 6th Aurora
第六名 6th Aurora
 
AI使能网络自动驾驶 AI Building Autonomous Driving Network
AI使能网络自动驾驶 AI Building Autonomous Driving NetworkAI使能网络自动驾驶 AI Building Autonomous Driving Network
AI使能网络自动驾驶 AI Building Autonomous Driving Network
 
2.2 go在阿里云cdn系统的应用
2.2 go在阿里云cdn系统的应用2.2 go在阿里云cdn系统的应用
2.2 go在阿里云cdn系统的应用
 
1.6 米嘉 gobuildweb
1.6 米嘉 gobuildweb1.6 米嘉 gobuildweb
1.6 米嘉 gobuildweb
 
1.4 go在数据存储上面的应用—毛剑
1.4 go在数据存储上面的应用—毛剑1.4 go在数据存储上面的应用—毛剑
1.4 go在数据存储上面的应用—毛剑
 
1.2 刘奇 go在分布式数据库中的应用
1.2 刘奇 go在分布式数据库中的应用1.2 刘奇 go在分布式数据库中的应用
1.2 刘奇 go在分布式数据库中的应用
 
Protocol libraries the right way
Protocol libraries the right wayProtocol libraries the right way
Protocol libraries the right way
 
美团数据库运维平台介绍
美团数据库运维平台介绍美团数据库运维平台介绍
美团数据库运维平台介绍
 
特卖场景下的大数据平台和机器学习实践
特卖场景下的大数据平台和机器学习实践特卖场景下的大数据平台和机器学习实践
特卖场景下的大数据平台和机器学习实践
 
我的互联网运维理论与实践
我的互联网运维理论与实践我的互联网运维理论与实践
我的互联网运维理论与实践
 
如何选择 Docker 监控方案
如何选择 Docker 监控方案如何选择 Docker 监控方案
如何选择 Docker 监控方案
 
美团数据库运维平台介绍
美团数据库运维平台介绍美团数据库运维平台介绍
美团数据库运维平台介绍
 
The net is dark and full of terrors - James Bennett
The net is dark and full of terrors - James BennettThe net is dark and full of terrors - James Bennett
The net is dark and full of terrors - James Bennett
 
Hypothesis randomised testing for django
Hypothesis randomised testing for djangoHypothesis randomised testing for django
Hypothesis randomised testing for django
 
NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享
NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享
NoSQL@VIP — 唯品会NoSQL平台⾃动化发展及运维经验分享
 

Python的module机制与最佳实践

  • 1. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Python Module 引⼊入机 制与最佳实践 —刘畅
  • 2. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 演讲内容 1. 为什么会发⽣生ImportError? 2. 如何避免 sys.path.append 的hack⾏行为? 3. python -m 是怎么回事? 4. python项⺫⽬目的⺫⽬目录结构如何组织? 5. import 语句是如何实现的? 6. 如何定制化⾃自⼰己的import?
  • 3. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Python的包引⼊入机制 • 
 
 
 import string
 print string.a a = 2 pkg/
 pkg/__init__.py
 pkg/main.py pkg/string.py • 如果运⾏行 pkg/main.py ⽂文件的话,会发⽣生什么事情? • import的string 是标准库的string还是当前⺫⽬目录的string呢? • 如果明确的指明⾃自⼰己要引⼊入的module?
  • 4. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Python的包引⼊入机制 • 相对引⽤用 implicit/explicit relative import • 绝对引⽤用 absolute import • 推荐阅读:https://docs.python.org/2/whatsnew/ 2.5.html#pep-328-absolute-and-relative-imports
  • 5. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! relative_import 代码⽰示例: from .foo import bar
 from ..foo import bar • 在 py2.7 之前是python的默认的module引⼊入⽅方式 • 推荐阅读:PEP0328
  • 6. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! absolute Import 代码⽰示例: from __future__ import absolute_import from API.foo import bar from API.API2.foo import bar • 在py2.5实现,需要明确的声明从 __future__ 中引⼊入该机制。在py2.7和 py3中成为默认的引⼊入机制。
  • 7. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 相对引⽤用!出错!
  • 8. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 绝对引⽤用!出错!
  • 9. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 如何解决引⽤用⽤用出错的问题?
  • 10. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! import sys sys.path.append(‘..’) 不推荐!
  • 11. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! ⼩小知识:加载python的⼆二 种⽅方式 • as the top-level script
 python foo/myfile.py • as a module:
 python -m foo.myfile
  • 12. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! ⼆二种加载⽅方式的不同之 处 When a file is loaded, it is given a name (which is stored in its __name__ attribute). If it was loaded as the top-level script, its name is __main__. If it was loaded as a module, its name is the filename, preceded by the names of any packages/ subpackages of which it is a part, separated by dots. • 推荐阅读:http://stackoverflow.com/questions/ 14132789/pythonrelative-imports-for-the-billionth- time
  • 13. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 为什么这个例⼦子出错?
  • 14. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 出错的原因 • 当使⽤用⽤用python subpackage1/foo.py时,foo.py 被当成了 top level script,它的__name__属性被设置为__main__。 • relative import则是根据__name__来确定被引⼊入⼊入包的位 置。 Relative imports use a module's __name__ attribute to determine that module's position in the package hierarchy. If the module’s name does not contain any package information (e.g. it is set to '__main__') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
  • 15. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 解决办法 • 让运⾏行⾏行的⽂文⽂文件有完整的 __name__,也就是使⽤用⽤用 module 的 load⽅方⽅方式 • 推荐阅读:PEP 0366
  • 16. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! ⼩小知识 • python在执⾏行⾏行程序时,会⾃自⾃自动的把CWD添加到 sys.path中。
  • 17. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 为什么这个例⼦子出错?
  • 18. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 出错的原因 • 例⼦子从subpackage1.bar中引⼊入hello函数。但是 sys.path中找不到subpackage1这个包。
  • 19. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 解决办法 • python -m subpackage1.foo2 • 将cwd修改为合适的⺫⽬目录 • 将subpackage1添加到 sys.path
  • 20. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 我们应该使⽤用哪种引⽤用⽅方式? pep8推荐absolute import 在特别复杂的包结构时, 也可以使⽤用明确的相对引⽤用 相对引⽤用(不带.号)在python3被移除
  • 21. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! facebook/tornado 的代码⽰示例
  • 22. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Python项⺫⽬目的推荐⺫⽬目录结构
  • 23. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! • 每个项⺫⽬目⼀一个⺫⽬目录(不是python package,没有 __init__.py) • 顶层⺫⽬目录下⾯面⼀一个同名(也可以不同名)的python package。 • ⽂文档⺫⽬目录在顶层⺫⽬目录下 • 各种脚本,配置⽂文件,描述⽂文件在顶层⺫⽬目录下(好处? 想⼀一想sys.path的进制)
  • 24. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! import 实现 > from package import module as mymodule 1. 查找相应的module 2. 加载module到local namespace
  • 25. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! 查找过程 1. 检查 sys.modules (保存了之前import的类库的缓存),如 果module被找到,则⾛走到第⼆二步。 2. 检查 sys.meta_path。meta_path 是⼀一个 list,⾥里⾯面保存着 ⼀一些  finder对象,如果找到该module的话,就会返回⼀一个 finder对象。 3.检查⼀一些隐式的finder对象,不同的python实现有不同的隐 式finder,但是都会有 sys.path_hooks, sys.path_importer_cache 以及 sys.path。 4.抛出 ImportError
  • 26. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! finder 对象 • finder 对象⽤用来查找module的 loader对象 • finder 必须实现⼀一个叫作 find_module 的⽅方法,当 find_module成功,返回 loader,否则返回None • 可以 python -m modulefinder filename.py,打印出 finder的具体信息 • 推荐阅读 https://docs.python.org/2/library/ modulefinder.html 和 PEP302
  • 27. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! loader对象 loader对象要实现load_module⽅方法 load_module(module)⽅方法会执⾏行: • 检查sys.module,如果有则返回,否则添加新 的。如果load失败,还要删除之前添加的module object • 设置__file__ __name__ __path__ __loader__ __package__
  • 28. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! ⼀一个meta_path的例⼦子 import sys class Watcher(object): @classmethod def find_module(cls, name, path, target=None): print('Importing', name, path, target) return None sys.meta_path.insert(0, Watcher) import socket Importing socket None None Importing _socket None None Importing enum None None Importing collections None None Importing _collections None None Importing operator None None Importing _operator None None Importing keyword None None Importing heapq None None Importing itertools None None Importing _heapq None None Importing reprlib None None Importing types None None
  • 29. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! sys.path • sys.path 由三个部分组成
  • 30. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! sys.prefix python 的安装⺫⽬目录
  • 31. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! PYTHONPATH 环境变量,类似于PATH
  • 32. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! site.py • 定义了 site-package 的⺫⽬目录 • 可以python -S关闭site.py的加载
  • 33. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! import hooks • __import__:内建函数 • imputil:py2.6开始被声明废弃,py3彻底移除 • importlib:py3中添加,同时添加到py2.7 • 推荐阅读:https://docs.python.org/3.5/library/ importlib.html
  • 34. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! import hook⽤用法 __import__ 的⽤用法,不推荐: In [1]: spam = __import__('sys')
 In [2]: spam
 Out[2]: <module 'sys' (built-in)> importlib 的⽤用法,推荐: # Same as: import spam
 In [1]: spam = importlib.import_module(‘spam') 
 # Same as: from . import spam
 In [2]: spam = importlib.import_module('.spam', __package__)
  • 35. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Hook Example >>> def my_import(modname *args, imp=__import__): ... print('importing', modname) ... return imp(modname, *args) ... >>> import builtins >>> builtins.__import__ = my_import >>> >>> import socket importing socket importing _socket … builtins 只⽀支持py3,上⾯面的例⼦子要在py3下运⾏行
  • 36. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Hook Example2 # spam.py
 from importlib.util import find_spec
 if find_spec('foo'):
 import foo
 else:
 import simplefoo 更加直观的⽅方式import包,出错信息更丰富
  • 37. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Hook Example3 • Lazy Importer • https://pypi.python.org/pypi/Importing
  • 38. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Hook Example4 • ⾃自定义⼀一个python package proxy • flask 的插件机制,当引⼊入flask.ext.api等包时⾃自动 import相应的包。
  • 39. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! flask 实现 def load_module(self, fullname): if fullname in sys.modules: return sys.modules[fullname] modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff] for path in self.module_choices: realname = path % modname try: __import__(realname) except ImportError: exc_type, exc_value, tb = sys.exc_info() sys.modules.pop(fullname, None) if self.is_important_traceback(realname, tb): reraise(exc_type, exc_value, tb.tb_next) continue module = sys.modules[fullname] = sys.modules[realname] if '.' not in modname: setattr(sys.modules[self.wrapper_module], modname, module) return module raise ImportError('No module named %s' % fullname)
  • 40. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Hook Example5 >>> import requests Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named ‘requests' >>> import autoinstall >>> import requests Installing requests >>> requests <module 'requests' from '...python3.4/site-packages/requests/ __init__.py’> 在import第三⽅方包时,⾃自动安装相应的依赖
  • 41. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! import sys import subprocess import importlib.util class AutoInstall(object): _loaded = set() @classmethod def find_spec(cls, name, path, target=None): if path is None!and name not in! cls._loaded: cls._loaded.add(name) print("Installing",! name) try: out = subprocess.check_output( [sys.executable, '-m', 'pip', 'install', name]) return importlib.util.find_spec(name) except Exception as e: print("Failed") return None sys.meta_path.append(AutoInstall)
  • 42. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Refrences • Modules and Packages:Live and Let Die! —David Beazley • http://stackoverflow.com/questions/14132789/pythonrelative-imports-for-the-billionth-time • https://github.com/tornadoweb/tornado • https://github.com/mitsuhiko/flask • PEP 0328 • PEP 0273 • PEP 0338 • PEP 0008 • PEP 0302 • PEP 0366 • PEP 0451
  • 43. 北京/上海/⼲⼴广州 0xFF Life's pathetic, go Pythonic! Thanks • 刘畅 • GeneDock Senior Software Engineer • http://liuchang0812.com • RocksDB/Spark/Tornado Contributor • https://genedock.com/joinus/