简单介绍 import
相信 小伙伴们 都 使用过
import A
from B import C
但是 很多 py源代码 中 经常使用到 import, 甚至 在 openstack 中 还有 更加复杂的 自动加载 模块 , 自动 import 创建实例
from oslo_utils import importutils
importutils.import_object("learn_import.C1")
importutils.import_class("learn_import.C1")
基本上 就是 根据字符串 动态的 加载 模块 加载 实例。
今天 我们 来讲讲 更 复杂 的 自动导入 class 到 指定 命名空间!
常规 import 放法
import 会返回 被引用的 模块对象
if __name__=='__main__':
print(globals())
be_imported = __import__("be_imported")
print(globals())
y1 = be_imported.Y1()
print(y1)
# output
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__file__': 'use_import.py', '__doc__': None, '__package__': None}
{'be_imported': <module 'be_imported' from '/root/cinder/.tox/local_test/__import__use/be_imported.pyc'>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'use_import.py', '__package__': None, '__name__': '__main__', '__doc__': None}
<be_imported.Y1 object at 0x7f27e3f6b650>
自动注册 class
可以 在 被引用 模块中添加 自动注册 函数 将自身 注册到 整个 globals
在 cinder 中 object 文件夹 下 init.py 有 很多 没有返回值的 improt object 对象。
但是 直接 import 在 globals 中 是没有 引用 的 , 也就是说 我们 访问不到 这些 匿名的 由 import 引入的对象。
那他们是怎么 注册进来的呢
首先阐述一下 我的观点, 这种 代码 写的 让 读者 很是崩溃,,,,
- register_all 注册所有 objects
/root/community/cinder/cinder/objects/init.py
def register_all():
# NOTE(danms): You must make sure your object gets imported in this
# function in order for it to be registered by services that may
# need to receive it via RPC.
__import__('cinder.objects.backup')
# NOTE(geguileo): Don't include cleanable to prevent circular imports
__import__('cinder.objects.cleanup_request')
__import__('cinder.objects.cgsnapshot')
匿名的 import 并不能 将函数 注册到 globals 命名空间中去
- 随便 挑选一个 cinder.objects.xxx 一探究竟
就是 一个 普通的 类 唯一不同 就是 多了 个 rigister 装饰器
@base.CinderObjectRegistry.register
class Cluster(base.CinderPersistentObject, base.CinderObject,
base.CinderComparableObject):
f12 跳转定义进去
- 查看 cinderPersistentObject
并没有发现 rigister 装饰器 ,必定 在其 父类中 , f12 进去看看
class CinderObjectRegistry(base.VersionedObjectRegistry):
def registration_hook(self, cls, index):
"""Hook called when registering a class.
This method takes care of adding the class to cinder.objects namespace.
Should registering class have a method called cinder_ovo_cls_init it
will be called to support class initialization. This is convenient
for all persistent classes that need to register their models.
"""
setattr(objects, cls.obj_name(), cls)
# If registering class has a callable initialization method, call it.
if isinstance(getattr(cls, 'cinder_ovo_cls_init', None),
abc.Callable):
cls.cinder_ovo_cls_init()
- 继续 查看 VersionedObjectRegistry
发现 rigister 装饰器 在 这个 父类中 ,但是实际 执行的时候 , 还是 执行了 一个 只有 pass 的 hook
所以 在 实际 调用中 还是 执行的 子类 中的 registration_hook
那么 就来 回头 详细 阅读一下 子类 registration_hook 的 详细内容
class VersionedObjectRegistry(object):
_registry = None
def __new__(cls, *args, **kwargs):
if not VersionedObjectRegistry._registry:
VersionedObjectRegistry._registry = object.__new__(
VersionedObjectRegistry, *args, **kwargs)
VersionedObjectRegistry._registry._obj_classes = \
collections.defaultdict(list)
self = object.__new__(cls, *args, **kwargs)
self._obj_classes = VersionedObjectRegistry._registry._obj_classes
return self
def registration_hook(self, cls, index):
pass
def _register_class(self, cls):
def _vers_tuple(obj):
return vutils.convert_version_to_tuple(obj.VERSION)
# ......
# Either this is the first time we've seen the object or it's
# an older version than anything we'e seen.
self._obj_classes[obj_name].append(cls)
self.registration_hook(cls, 0)
@classmethod
def register(cls, obj_cls):
registry = cls()
registry._register_class(obj_cls)
return obj_cls
- 详细阅读 CinderObjectRegistry.registration_hook
代码 都用看 最重要的 就是 这一句 注释
This method takes care of adding the class to cinder.objects namespace.
简单 来讲 废了 这么大 周折 就是 为了 把 函数 注册到 cinder.objects 命名空间 去
def registration_hook(self, cls, index):
"""Hook called when registering a class.
This method takes care of adding the class to cinder.objects namespace.
Should registering class have a method called cinder_ovo_cls_init it
will be called to support class initialization. This is convenient
for all persistent classes that need to register their models.
"""
setattr(objects, cls.obj_name(), cls)
# If registering class has a callable initialization method, call it.
if isinstance(getattr(cls, 'cinder_ovo_cls_init', None),
abc.Callable):
cls.cinder_ovo_cls_init()
结尾
那么 读了 这么多 , 动态 注册 这些 class 干嘛呢?
再去 回顾以下 register_all() 函数 的 注释
简单讲 就是 rpc 要用, rpc 是什么 远程过程调用, 在 openstack 中 基本 就是指 详细 队列 的 rpc api 中会用到
rpc api 用 这些 东西干嘛呢, 下一篇笔记中 继续读。
def register_all():
# NOTE(danms): You must make sure your object gets imported in this
# function in order for it to be registered by services that may
# need to receive it via RPC.