1. import_hook
所谓import hook
就是指直接自定义finder和loader,并将finder放入导入过程,以实现一些特殊的运行时行为的技巧.
利用这个可以做到很多非常神奇的事情,比如
- import某个特定模块时触发某个回调函数来通知我们
- import一个远程服务器上的模块
- 直接import其他语言的模块来使用
本节需要的先验知识包括:
1.1. import hook的基本形式
import hook通常是以一个单文件模块的形式出现的,其中的过程说白了就是自定义finder和loader,因此自定义这两个类都是必须的,然后就是将定义的finder实例化,并将这个实例加入sys.meta_path
.下面是模板代码.
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict class ClientImportLoader(Loader): @classmethod def create_module(clz,spec): """用于创建模块的.""" module = __create_module_from_spec(spec) return module or None @classmethod def exec_module (clz, module): """每次执行引入模块或者重载模块时会执行的操作""" pass loader= ClientImportLoader() class ClientImportFinder(MetaPathFinder): @classmethod def find_spec (klass, full_name, paths=None, target=None): """查找模块的逻辑""" pass return ModuleSpec(full_name, loader, origin=module_full_path) sys.meta_path.insert(0, ClientImportFinder())
当这个定义import hook的模块被加载后,他就可以正常的执行自己的功能了,因此通常这个import hook的模块需要优先加载.
1.2. import某个特定模块时触发某个回调函数来通知我们
这个例子来自python cookbook,不过上面的代码已经比较过时了,这边给出python3.5+推荐的写法
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
import numpy
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
import a
********None
finder._skip
set()
为了避免陷入无线循环,ClientImportFinder维护了一个所有被加载过的模块集合_skip
,如果一个模块在加载过程中又有另一个地方来加载,那么就会跳过这个加载器
1.3. import一个远程服务器上的模块
这个例子主要是复写finder以可以查找到目标服务器上的模块文件.同时复写loader的create_module方法用远端的代码生成服务.
我们的远程代码以http服务的形式放在静态服务器上
testcode-| |-spam.py |-fib.py |-grok-| |-__init__.py |-blah.py
spam.py
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
0
fib.py
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
1
grok/__init__.py
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
2
grok/blah.py
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
3
使用python自带的http服务启动:
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
4
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
5
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
6
1.3.1. 最简单的方法
这个流程也描述了最通用的模块导入流程.我们可以使用imp.new_module
新建一个空的模块对象,再使用内置方法compile()
将源码编译到一个代码对象中,然后在模块对象的字典中来执行它.
这种方式没有嵌入到通常的import语句中,如果要支持更高级的结构比如包就需要更多的工作了.
下面是使用这个函数的方式:
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
7
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
8
import importlib from importlib.abc import ( MetaPathFinder, PathEntryFinder, Loader ) from importlib.machinery import ModuleSpec import sys from collections import defaultdict _post_import_hooks = defaultdict(list) class ClientImportLoader(Loader): def __init__(self, finder): self._finder = finder def create_module(self,spec): """这边只要调用父类的实现即可.""" return super().create_module(spec) def exec_module (self, module): """在_post_import_hooks中查找对应模块中的回调函数并执行.""" for func in _post_import_hooks[module.__name__]: func(module) self._finder._skip.remove(module.__name__) class ClientImportFinder(MetaPathFinder): def __init__(self): self._skip = set() def find_spec(self, full_name, paths=None, target=None): if full_name in self._skip: return None self._skip.add(full_name) loader = ClientImportLoader(self) return ModuleSpec(full_name, loader, origin=paths) def when_imported(fullname): def decorate(func): if fullname in sys.modules: func(sys.modules[fullname]) else: _post_import_hooks[fullname].append(func) return func return decorate finder = ClientImportFinder() sys.meta_path.insert(0, finder)
9
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
0
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
1
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
2
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
3
1.3.2. 使用 import hook实现隐式调用远端模块
如果我们想要导入文件系统中某个文件作为模块,我们会这样写以确保文件目录Python解释器可以找到.
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
4
我们希望远端的也可以实现这种与标准流程统一的方式,这时候就需要使用import hook了.
因为访问的地址和文件系统不同,因此可以使用sys.path_hooks
为这个特定的地址设置一个finder
此处我们需要
- 定义finder
- 定义loader
定义handle_url()函数作为钩子
sys.path_hooks.append(handle_url)
变量中注册着查找sys.path
的钩子,当sys.path
的实体被处理时会调用sys.path_hooks
中的函数.如果任何一个函数返回了一个finder,那么这个对象就被用来为sys.path
实体加载模块.
这个例子完全使用标准库实现
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
5
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
6
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
7
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
8
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
9
import numpy
0
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
7
import numpy
2
import numpy
3
@when_imported('numpy') def warn_numpy(mod): print('numpy? Are you crazy?')
7
import numpy
5
import numpy
6
1.4. 直接import其他语言的模块来使用
一个更加酷的用法是直接导入别的语言的代码成为模块.这有两种途径
- 通过一个自定义的编译器将源码编译为python可以直接import的动态链接库,之后再导入这个动态链接库
- 通过一个自定义的语法解释器将特定源码转化为python对象
1.4.1. 导入Fortran代码作为模块
这个例子我们来尝试第一种方法,其基本流程是:
定义一个finder用于找到以
.f
,.f90
或者.f95
位后缀的文件作为源文件需要注意的这个需求依然会用到文件系统,Fortran的源码很大的可能性与python的原生源文件共存,因此不适合使用
sys.path_hook
.使用numpy中自带的工具
f2py
来实现Fortran代码的编译工作f2py
无法指定输出的动态链接库位置,需要进一步的文件系统操作.这边可以利用标准库Pathlib
和shutil
将编译成功后的动态链接库导入到程序中.
需要注意引入动态链接库时不能使用
import
或者__import__
或者importlib.import_module
这些直接生成完整模块对象的方式,否则会递归调用. 此处应该使用如下的方式借由生成spec来生成模块.import numpy
7再借由这个spec的loader来执行模块
import numpy
8
当然了这个例子并没有考虑fortain本身的语法和多文件编译的问题,可能还会有些问题.
import numpy
9
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
0
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
1
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
2
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
3
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
4
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
5
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
6
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
7
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
8
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
9
import a
0
import a
1
import a
2
********None ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', '_finder', 'create_module', 'exec_module', 'load_module', 'module_repr']
9
1.5. 使用警告
import hook是python中非常高级的语言技巧,并不提倡用户使用,如果非要使用,请使用warnning
在导入时提醒用户
还没有评论,来说两句吧...