共计 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 里的反射 (自学习)
以上
正文完