Python应用反射实现Excel与对象之间的转换

代码在最下方,伸手党间接滚动到最初

场景

须要从Excel中加载到内存中,转换为class对象执行操作

环境

  • Python3.8
  • openpyxl==3.0.5

前置常识

反射(仅介绍这个帮忙类用到的几个反射办法)

setattr、getattr

class Person():    name = None    def __init__(self, name):        self.name = namep = Person("laowang")n = getattr(p, "name")print(n)# 打印后果:    laowangsetattr(p, "name", "laoxu")n2 = getattr(p, "name")print(n2)# 打印后果:   laoxu

反射实例化对象

class Person():    name = None    def print_sth(self):        print("测试实例化办法", self.name)def test(clazz):    """    实例化对象    :param clazz: 要实例化的类型     """    x = clazz()    setattr(x, "name", "老王")    x.print_sth()    # 打印:    测试实例化办法 老王test(Person)

Excel操作类库 - openpyxl

创立Excel

from openpyxl import Workbookwb = Workbook()ws1 = wb.activews1.append(['name', 'age', 'isManager', 'remark'])ws1.append(['', '', '', ' '])ws1.append(['老王', '18', 'false', '  '])ws1.append(['laoxu', '28.4', 'TRUE', 'zaoricaifuziyou'])ws1.append(['', '', '', ' '])ws2 = wb.create_sheet("ws2")ws2.append(['name', 'age', 'isManager', 'remark'])ws2.append(['小李', '50', 'TRuE', 'fly knife'])ws2.append(['', '', '', ' '])ws2.append(['aaa', '11', 'false', 'hehehe'])wb.save("test_convert_2_class.xlsx")

读取Excel

from openpyxl import Workbookdef print_row(arr):    """为了显示不便,打印行"""    for item in arr:        print(item,end="\t\t|")    print()# 读取上一个代码块创立的Excel代码work_book = load_workbook("test_convert_2_class.xlsx")result = []for sheet_name in work_book.sheetnames:    print("-----------------",sheet_name,"-----------------")    ws = work_book[sheet_name]    # 获取表头    table_heads = []    for title_row in ws.iter_rows(max_row=1):        for cell in title_row:            table_heads.append(cell.value)    print_row(table_heads)    # 获取表数据    table = []    for row in ws.iter_rows(min_row=2):        row_data=[]        for column_index in range(len(row)):            row_data.append(row[column_index].value)        print_row(row_data)# 打印后果如下:# ----------------- Sheet -----------------# name        |age        |isManager        |remark        |# None        |None        |None        |         |# 老王        |18        |false        |          |# laoxu        |28.4        |TRUE        |zaoricaifuziyou        |# None        |None        |None        |         |# ----------------- ws2 -----------------# name        |age        |isManager        |remark        |# 小李        |50        |TRuE        |fly knife        |# None        |None        |None        |         |# aaa        |11        |false        |hehehe        |

伸手党代码

excel_helper.py

import osimport refrom os.path import isfilefrom openpyxl import load_workbook, Workbookdef _convert_value(value):    """    将单元格中数据,辨别根本类型    相似"true"/"false"(不辨别大小写)转换为bool值    长得像数字的转换为float类型    其余(空格、空行)转换为None    :param value: 单元格的值    :return: 转换后的类型    """    value_str = str(value).lower()    if value_str == 'true':        return True    elif value_str == 'false':        return False    elif re.match(r"^[+|-]?\d+.?\d*$", value_str):        return float(value_str)    elif re.match(r"^\s*$", value_str):        return None    else:        return valueclass ExcelHelper:    """    Excel帮忙类    """    @classmethod    def convert_2_class(cls, file_path, clazz):        """        转换为class,可转换多张sheet表,转换为对立clazz对象        过滤掉为空行        :param file_path:Excel文件门路        :param clazz:后果转换为clazz对象        :return: 对象列表的列表,构造为[[clazz(),clazz()],[clazz()]]        """        if not file_path.endswith(".xlsx"):            raise ValueError("文件必须为.xlsx结尾的Excel文件")        if not isfile(file_path):            raise FileNotFoundError("文件门路 {0} 不存在".format(file_path))        work_book = load_workbook(file_path)        result = []        for sheet_name in work_book.sheetnames:            ws = work_book[sheet_name]            # 获取表头            table_heads = []            for title_row in ws.iter_rows(max_row=1):                for cell in title_row:                    table_heads.append(cell.value)            # 获取表数据            table = []            for row in ws.iter_rows(min_row=2):                # 实例化对象                instance = clazz()                for column_index in range(len(row)):                    setattr(instance, table_heads[column_index], _convert_value(row[column_index].value))                # 过滤空行(所有属性均为None的对象)                is_valid = False                for attr in instance.__dict__:                    if not attr.startswith("_") and instance.__dict__[attr] is not None:                        is_valid = True                        break                if is_valid:                    table.append(instance)            result.append(table)        return result    @classmethod    def save(cls, file_path, tables):        if not file_path.endswith(".xlsx"):            raise ValueError("文件必须为.xlsx结尾的Excel文件")        work_book = Workbook()        is_first = True        for table in tables:            if is_first:                ws = work_book.active                is_first = False            else:                ws = work_book.create_sheet()            # 增加表头            table_heads = []            for attr in table[0].__dict__:                # 过滤"_"结尾的属性                if not attr.startswith("_"):                    table_heads.append(attr)            ws.append(table_heads)            # 增加数据            for row in table:                data = []                for head in table_heads:                    data.append(getattr(row, head))                ws.append(data)        try:            # 生成保留文件夹门路            folder_index = max(file_path.rfind("\\"), file_path.rfind("/"))            if folder_index != -1:                folder_path = file_path[0:folder_index]                if not os.path.exists(folder_path):                    os.mkdir(folder_path)            work_book.save(file_path)        except Exception:            raise OSError("创立Excel失败")

应用办法

# 导入类from excel_helper import ExcelHelper# 示例对象class A:    name=None    age=None    isManager=None# 读取Excel文件,并转换为指定类型对象列表tables = ExcelHelper.convert_2_class("123.xlsx", A)# 保留为Excela1=A()table=[a1]ExcelHelper.save("456.xls", [table])

留神

  • 该帮忙类均为@classmethod
  • 该帮忙类应用反射实现,所以表头名称须要与对象的字段名一一对应(如代码中的class A 与 下表"表1-1")
  • Excel中能够有多张表(sheet tab),所以参数为对象列表的列表,请留神对应关系
  • 以后读取Excel仅能转换为一个class类型,所以多种表构造请应用多张表

表1-1

nameageisManager
老王18True
Tom28FALSE

参考资料

PYTHON里的反射(自学习)

以上