python 详细理解 import ---- openstack自动import class 到 特定命名空间

简介: python 详细理解 import ---- openstack自动import class 到 特定命名空间

简单介绍 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 命名空间中去

  1. 随便 挑选一个 cinder.objects.xxx 一探究竟
    就是 一个 普通的 类 唯一不同 就是 多了 个 rigister 装饰器
@base.CinderObjectRegistry.register
class Cluster(base.CinderPersistentObject, base.CinderObject,
              base.CinderComparableObject):


f12 跳转定义进去

  1. 查看 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()
  1. 继续 查看 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

5.详细阅读 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.
目录
打赏
0
0
0
0
3
分享
相关文章
|
14天前
|
The Instance Class Static Magic Method in Python
So what is the difference between the Instance method, the Class method and the Static method?
24 8
Python进阶:深入理解import机制与importlib的妙用
本文深入解析了Python的`import`机制及其背后的原理,涵盖基本用法、模块缓存、导入搜索路径和导入钩子等内容。通过理解这些机制,开发者可以优化模块加载速度并确保代码的一致性。文章还介绍了`importlib`的强大功能,如动态模块导入、实现插件系统及重新加载模块,展示了如何利用这些特性编写更加灵活和高效的代码。掌握这些知识有助于提升编程技能,充分利用Python的强大功能。
28 4
【Python】已完美解决:ImportError: cannot import name ‘Imputer‘ from ‘sklearn.preprocessing
【Python】已完美解决:ImportError: cannot import name ‘Imputer‘ from ‘sklearn.preprocessing
479 3
|
4月前
|
Python Tricks: String Conversion(Every Class Needs a ___repr__)
Python Tricks: String Conversion(Every Class Needs a ___repr__)
33 5
写Python时不用import,你会遭遇什么
写Python时不用import,你会遭遇什么
41 2
|
6月前
|
深入解析 Python中的命名空间和作用域并举例
【8月更文挑战第15天】Python中的命名空间与作用域是理解变量组织与访问的核心。命名空间是名称到对象的映射,分为全局、局部和内置三种。作用域定义变量的可访问范围,遵循LEGB规则:局部(L)、闭包(E)、全局(G)、内置(B)。示例展示了如何通过`nonlocal`声明跨作用域修改变量。这些机制确保了变量的有效管理和代码的高效执行。
66 0
|
6月前
|
Python中的命名空间
【8月更文挑战第8天】本文深入探讨了Python中命名空间与作用域的概念及其应用。命名空间管理变量名与对象间的映射关系,分为全局、局部及内建三种。全局命名空间包含模块顶层定义的变量,局部命名空间则由函数内部定义的变量构成,内建命名空间包括Python的所有内置函数与异常。作用域规定了变量的可见范围,包括全局、局部、嵌套及内建作用域。文章通过多个代码示例展示了不同作用域下的变量访问规则,如局部变量仅在函数内部有效,而全局变量可在整个模块中访问。此外,还介绍了作用域链的查找机制、如何通过`global`和`nonlocal`关键字修改不同作用域内的变量、如何利用闭包访问外部函数的局部变量。
47 0
Python的`import`用于加载模块,基础形式是`import module`,全量导入
【6月更文挑战第23天】Python的`import`用于加载模块,基础形式是`import module`,全量导入;`from module import name`选择性导入部分,减少命名空间污染;`from module import *`导入所有(不推荐),易引发冲突。别名导入如`from math import sqrt as square_root`可避免冲突。包导入用`.`,如`import xml.etree.ElementTree as ET`。
86 8
在Python中,导入其他模块是通过使用import语句完成的
在Python中导入模块涉及`import`语句的不同用法:1) `import math`导入整个标准库;2) `from math import sqrt`导入单个函数;3) `import numpy as np`使用别名;4) `from random import *`导入所有(不推荐);5) `import xml.etree.ElementTree as ET`导入子模块;6) 使用`importlib.import_module()`延迟导入;7) `from .module import func`导入相对路径模块,需管理`sys.path`。
116 6
Python模块导入包括:`import math`导入标准库
【6月更文挑战第23天】Python模块导入包括:`import math`导入标准库,`from math import sqrt`导入单个函数,`import numpy as np`给模块取别名,`from random import *`导入所有(不推荐),`import xml.etree.ElementTree as ET`导入子模块,`import_module(&#39;pandas&#39;)`按需导入,和使用相对路径如`from .module import func`处理项目结构。记得调整`sys.path`以包含自定义模块路径。
135 4

热门文章

最新文章