狠狠撸

狠狠撸Share a Scribd company logo
北京/上海/??广州 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/my?le.py
? as a module:?
python -m foo.my?le
北京/上海/??广州 0xFF Life's pathetic, go Pythonic!
?二种加载?方式的不同之
处
When a ?le 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 ?lename,
preceded by the names of any packages/
subpackages of which it is a part, separated by dots.
? 推荐阅读:http://stackover?ow.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 ?le
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,?里?面保存着
?一些? ?nder对象,如果找到该module的话,就会返回?一个
?nder对象。
3.检查?一些隐式的?nder对象,不同的python实现有不同的隐
式?nder,但是都会有 sys.path_hooks,
sys.path_importer_cache 以及 sys.path。
4.抛出 ImportError
北京/上海/??广州 0xFF Life's pathetic, go Pythonic!
?nder 对象
? ?nder 对象?用来查找module的 loader对象
? ?nder 必须实现?一个叫作 ?nd_module 的?方法,当
?nd_module成功,返回 loader,否则返回None
? 可以 python -m module?nder ?lename.py,打印出
?nder的具体信息
? 推荐阅读 https://docs.python.org/2/library/
module?nder.html 和 PEP302
北京/上海/??广州 0xFF Life's pathetic, go Pythonic!
loader对象
loader对象要实现load_module?方法
load_module(module)?方法会执?行:
? 检查sys.module,如果有则返回,否则添加新
的。如果load失败,还要删除之前添加的module
object
? 设置__?le__ __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.pre?x
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
? ?ask 的插件机制,当引?入?ask.ext.api等包时?自动
import相应的包。
北京/上海/??广州 0xFF Life's pathetic, go Pythonic!
?ask 实现
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://stackover?ow.com/questions/14132789/pythonrelative-imports-for-the-billionth-time
? https://github.com/tornadoweb/tornado
? https://github.com/mitsuhiko/?ask
? 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

笔测迟丑辞苍的尘辞诲耻濒别机制与最佳实践

  • 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/my?le.py ? as a module:? python -m foo.my?le
  • 12. 北京/上海/??广州 0xFF Life's pathetic, go Pythonic! ?二种加载?方式的不同之 处 When a ?le 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 ?lename, preceded by the names of any packages/ subpackages of which it is a part, separated by dots. ? 推荐阅读:http://stackover?ow.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 ?le 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,?里?面保存着 ?一些? ?nder对象,如果找到该module的话,就会返回?一个 ?nder对象。 3.检查?一些隐式的?nder对象,不同的python实现有不同的隐 式?nder,但是都会有 sys.path_hooks, sys.path_importer_cache 以及 sys.path。 4.抛出 ImportError
  • 26. 北京/上海/??广州 0xFF Life's pathetic, go Pythonic! ?nder 对象 ? ?nder 对象?用来查找module的 loader对象 ? ?nder 必须实现?一个叫作 ?nd_module 的?方法,当 ?nd_module成功,返回 loader,否则返回None ? 可以 python -m module?nder ?lename.py,打印出 ?nder的具体信息 ? 推荐阅读 https://docs.python.org/2/library/ module?nder.html 和 PEP302
  • 27. 北京/上海/??广州 0xFF Life's pathetic, go Pythonic! loader对象 loader对象要实现load_module?方法 load_module(module)?方法会执?行: ? 检查sys.module,如果有则返回,否则添加新 的。如果load失败,还要删除之前添加的module object ? 设置__?le__ __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.pre?x 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 ? ?ask 的插件机制,当引?入?ask.ext.api等包时?自动 import相应的包。
  • 39. 北京/上海/??广州 0xFF Life's pathetic, go Pythonic! ?ask 实现 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://stackover?ow.com/questions/14132789/pythonrelative-imports-for-the-billionth-time ? https://github.com/tornadoweb/tornado ? https://github.com/mitsuhiko/?ask ? 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/