python:读取XML的类名并反射创建类对象(工厂模式)

需求

根据传递的参数到xml中查找全类名,并反射获取类对象。这样对外只提供一个接口即可创建不同类的对象。

实现

此功能实现分为两部分:读取xml和反射获取类对象。

解析xml文件

python解析xml网上有很多教程,本文不再说明,可参考:Python XML 解析 

xml内容描述

公司项目的业务场景是:针对不同业务类型的excel文件,开发对应的pandas处理逻辑,每个处理逻辑封装成一个类。用户在调用处理程序时,仅需要传递一个excel的类型参数,程序根据参数到xml中查找对应的类名,反射创建类对象处理excel。

所以,项目中的xml文件大致是这样的:

在根元素class下,包含多个二级元素,二级元素的title表示excel的业务类型,如各种品牌的GMV使用不同格式的excel,所以使用不同的处理逻辑来处理这些excel。每种类型的处理逻辑封装在一个类中,这些而三级元素path则用于描述这些类的类名。

<!-- 根元素  -->
<collection shelf="class">
    <!-- 一级子元素class,用于描述业务逻辑类型的详细信息 -->
    <class title="lancome">
        <!-- 二级子元素path,用于描述对应类的类名 -->
        <path>ClassOne</path>
    </class>
    <!-- 一级子元素class,用于描述项目中的类与类路径 -->
    <class title="loreal">
       <path>ClassTwo</path>
    </class>
</collection>

读取xml

1.使用minidom解析器打开 XML 文档

from xml.dom.minidom import parse

    DOMTree = parse(xmlPath)
    collection = DOMTree.documentElement

输出:
    <DOM Element: collection at 0x242b71e6178>

 2.获取名为class的根元素

    xmlDocument = collection.getElementsByTagName("class")

输出:
    [<DOM Element: class at 0x1fe01546638>, <DOM Element: class at 0x1fe01546800>]

 3.在根元素中查找title为type1的二级元素,并获取其名为path的三级元素

   # 遍历所有节点的详细信息中搜索二级节点
    for eName in classes:
        if eName.getAttribute("title") == "type1":
            # 获取名为path的所有子节点中的第一个子节点的第一个子节点(拗口哟)
            className = eName.getElementsByTagName("path")[0].childNodes[0].data
            break
    print("className")


    输出:
        ClassOne

 

反射获取类对象

python中可使用getattr方法获取模块中的子模块,getattr方法包含两个参数:模块名,子模块名。

getattr会在指定模块中查找指定的子模块名,如:项目中有一个DataCompare模块,在这个模块中有一个ClassOne.py文件,在ClassOne.py文件中定义了一个名为ClassOne的类。

那么,可以使用getattr方法在DataCompare中查找ClassOne.py:

module = getattr(DataCompare, "ClassOne")

输出:
<module 'DataCompare.ClassOne' from 'E:\\code\\python\\Loreal\\DataCompare\\ClassOne.py'>

然后在ClassOne.py中查找ClassOne类:

getattr(module ,"ClassOne")

输出:
<class 'DataCompare.ClassOne.ClassOne'>

getattr(getattr(DataCompare,"ClassOne"), "ClassOne")

代码

将读取xml功能封装在一个XmlUtils.py工具模块中:

# -*- coding: UTF-8 -*-

from xml.dom.minidom import parse

def getXML(xmlPath):
    # 使用minidom解析器打开 XML 文档
    DOMTree = parse(xmlPath)
    collection = DOMTree.documentElement
    return collection


def getElement(xml, name1st, name2st):
    # 获取xml对象中一级节点的所有子节点集合
    classes = xml.getElementsByTagName(name1st)
    className = None
    # 遍历所有节点的详细信息中搜索二级节点
    for eName in classes:
        if eName.getAttribute("title") == name2st:
            # 获取名为path的所有子节点中的第一个子节点的第一个子节点(拗口哟)
            className = eName.getElementsByTagName("path")[0].childNodes[0].data
            break
    if className == None:
        print("没有找到这个类,请检查xml文件")
    return className

将反射创建类对象封装进一个ClassHandler.py中:

# -*- coding: UTF-8 -*-

import DataCompare
import DataCompare.XmlUtils as xml

class ClassHandler:

    def getClass(className):
        #getattr有两个参数,左边参数是模块名,右边参数是模块中的子模块名
        #getattr返回的是类对象,加个()创建实例
        return getattr(getattr(DataCompare,className), className)()

需要在DataCompare模块的__init__.py文件中导入DataCompare中的ClassOne等模块,否则会报找不到类对象的错误。__init__.py文件用于一些初始化工作,它不仅表示一个文件夹是一个python包,还会在导入这个模块时执行__init__.py里的代码:

#init文件用于标识这是一个py包,并且可以在init文件中导入包或者做一些初始化工作,这样在导入这个包时会自动执行
from DataCompare import ClassOne,ClassTwo

测试

在ClassOne中定义WhoAmI方法打印测试:

class ClassOne:
    def WhoAmI(self):
        print("class 1")

 调用XmlUtils读取xml,并使用返回的类名调用ClassHandler创建实例:

# -*- coding: UTF-8 -*-

import DataCompare.ClassHandler as ch
from  DataCompare.XmlUtils  import *

class ClassHandler:

    if __name__ == '__main__':
        className = getElement(getXML("conf/class.xml"),"class","type1")
        excutor = ch.getClass(className)
        excutor.WhoAmI()


输出:
    class 1

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值