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.
相关文章
|
4月前
|
数据采集 机器学习/深度学习 Python
【Python】已完美解决:ImportError: cannot import name ‘Imputer‘ from ‘sklearn.preprocessing
【Python】已完美解决:ImportError: cannot import name ‘Imputer‘ from ‘sklearn.preprocessing
312 3
|
1月前
|
Go C++ Python
Python Tricks: String Conversion(Every Class Needs a ___repr__)
Python Tricks: String Conversion(Every Class Needs a ___repr__)
|
3月前
|
算法 TensorFlow 算法框架/工具
写Python时不用import,你会遭遇什么
写Python时不用import,你会遭遇什么
|
3月前
|
Python
深入解析 Python中的命名空间和作用域并举例
【8月更文挑战第15天】Python中的命名空间与作用域是理解变量组织与访问的核心。命名空间是名称到对象的映射,分为全局、局部和内置三种。作用域定义变量的可访问范围,遵循LEGB规则:局部(L)、闭包(E)、全局(G)、内置(B)。示例展示了如何通过`nonlocal`声明跨作用域修改变量。这些机制确保了变量的有效管理和代码的高效执行。
48 0
|
3月前
|
存储 Python
Python中的命名空间
【8月更文挑战第8天】本文深入探讨了Python中命名空间与作用域的概念及其应用。命名空间管理变量名与对象间的映射关系,分为全局、局部及内建三种。全局命名空间包含模块顶层定义的变量,局部命名空间则由函数内部定义的变量构成,内建命名空间包括Python的所有内置函数与异常。作用域规定了变量的可见范围,包括全局、局部、嵌套及内建作用域。文章通过多个代码示例展示了不同作用域下的变量访问规则,如局部变量仅在函数内部有效,而全局变量可在整个模块中访问。此外,还介绍了作用域链的查找机制、如何通过`global`和`nonlocal`关键字修改不同作用域内的变量、如何利用闭包访问外部函数的局部变量。
27 0
|
5月前
|
XML 数据格式 Python
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`。
62 8
|
5月前
|
XML 数据格式 Python
在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`。
91 6
|
5月前
|
XML 数据格式 Python
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`以包含自定义模块路径。
95 4
|
5月前
|
存储 Python
Python教程:深入理解Python中的命名空间和作用域
在 Python 编程中,理解命名空间(Namespace)和作用域(Scope)是至关重要的。它们决定了变量和函数的可见性和访问性,并直接影响代码的结构和行为。本文将深入探讨 Python 3 中命名空间和作用域的概念、规则以及相关的高级主题。
82 4
|
5月前
|
存储 Python
Python的命名空间和作用域分析
在Python中,命名空间(Namespace)是用来存储变量名和对象引用之间映射关系的字典,而作用域(Scope)是指程序中变量可以被访问的区域范围。Python中的命名空间是用来存储变量名和对象引用之间映射关系的字典,Python中存在3种命名空间:内置命名空间、全局命名空间和局部命名空间。Python中存在3种命名空间:内置命名空间、全局命名空间和局部命名空间。局部作用域:由局部命名空间定义,在函数内部定义的变量只能在该函数内部访问。在全局命名空间中定义的变量可以在模块内的任何函数或类中直接使用。
49 3