关于python:Python使用反射实现Excel与对象之间的转换

34次阅读

共计 4419 个字符,预计需要花费 12 分钟才能阅读完成。

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

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

场景

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

环境

  • Python3.8
  • openpyxl==3.0.5

前置常识

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

setattr、getattr

class Person():
    name = None

    def __init__(self, name):
        self.name = name


p = Person("laowang")
n = getattr(p, "name")
print(n)
# 打印后果:laowang

setattr(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 Workbook


wb = Workbook()
ws1 = wb.active

ws1.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 Workbook

def 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 os
import re
from os.path import isfile

from openpyxl import load_workbook, Workbook


def _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 value


class 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)

# 保留为 Excel
a1=A()
table=[a1]
ExcelHelper.save("456.xls", [table])

留神

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

表 1 -1

name age isManager
老王 18 True
Tom 28 FALSE

参考资料

PYTHON 里的反射 (自学习)

以上

正文完
 0