点我展开:基本信息
提升class的可读性、可维护性、可测试性、可扩展性,贴近大厂工程标准。
代码对比
下面有两份代码,修改前与修改后,通过对比来理解相关修改处的作用。
下面为一个class中的方法,修改前:
def identify_specific_tables(
self,
documentation_file_path: str,
) -> str:
logger.info("Extract the Word standard table extraction")
doc = Document(documentation_file_path)
tables = []
for table in doc.tables:
data = []
for row in table.rows:
row_data = [cell.text.strip() for cell in row.cells]
data.append(row_data)
df = pd.DataFrame(data)
tables.append(df)
tables_str = [df.to_string() for df in tables]
prompt = f"""
以下用三个反引号分隔的是从“云南电网有限责任公司安全文明施工费使用管理标准(试行).docx”Word文档中提取的所有表格,\
Word文档提取表格集合:'''{tables_str}'''\
各个表格以DataFrame格式存储在一个列表中,遍历所有所有表格,提取出特定表格“电网工程建设安全文明施工费使用指导模板”,并输出提取出的表格,
该表格的特征如下:
1. 该表格包含['费用类型及取费建议', '措施类别及使用范围', '措施类别编码', '是否可摊销', '变电', '线路', '配网'],7列指标;
2. 其中'费用类型及取费建议'包含['安全生产费', '文明施工费', '环境保护费']三类费用信息;
注意:
- 直接返回表格信息,不要输出实现该功能的python代码;
返回说明:返回特定表格的信息,不要求文字说明。
"""
messages = [
{"role": "user", "content": prompt}
]
response = llm_client.chat(
model=LLM_MODEL,
messages=messages,
stream=False, # 不启用流式输出
)
return response.choices[0].message.content.strip()
将其单列出来作为一个class进行修改,修改后:
import logging
from typing import List
import pandas as pd
from docx import Document
from openai import OpenAI
from core.config import LLM_MODEL
from utils.logger import logger
# 如果 llm_client 已经在别处初始化,则直接导入;否则在此处初始化
# from clients.llm_client import llm_client
class DocumentationExtractor:
"""
从 Word 文档中提取并识别特定表格的通用工具类。
"""
@staticmethod
def _extract_all_tables(file_path: str) -> List[pd.DataFrame]:
"""
读取 Word 文档中的所有表格,并统一转换为 pandas.DataFrame 列表。
Args:
file_path (str): Word 文档绝对路径。
Returns:
List[pd.DataFrame]: 文档中所有表格对应的 DataFrame 列表。
"""
doc = Document(file_path)
tables: List[pd.DataFrame] = []
for table in doc.tables:
rows = [
[cell.text.strip() for cell in row.cells]
for row in table.rows
]
tables.append(pd.DataFrame(rows))
return tables
@classmethod
def identify_specific_table(
cls,
file_path: str,
*,
expected_columns: List[str],
expected_fee_types: List[str],
) -> str:
"""
识别并返回指定特征的安全文明施工费使用指导模板表格。
说明:
- 该方法为大模型调用入口,所有业务特征通过参数传入,保持通用性。
- 仅返回表格纯文本,不包含任何代码或额外说明。
Args:
file_path (str): Word 文档路径,必需为绝对路径。
expected_columns (List[str]): 目标表必须包含的列名列表。
expected_fee_types (List[str]): “费用类型及取费建议”列必须包含的费用类别列表。
Returns:
str: 符合特征的表格文本;若未匹配则返回空字符串。
"""
logger.info(
"Start extracting specific table from Word document",
extra={"file_path": file_path},
)
# 1. 提取所有表格
tables = cls._extract_all_tables(file_path)
tables_str = "\n\n".join(df.to_string(index=False) for df in tables)
# 2. 构造大模型 prompt
prompt = f"""
以下是从 Word 文档中提取的所有表格内容,已按 DataFrame 格式拼接:
{tables_str}
请依据以下规则,在所有表格中精确匹配并返回“电网工程建设安全文明施工费使用指导模板”:
规则:
1. 表格必须包含以下列(顺序无关):
{", ".join(expected_columns)}
2. “费用类型及取费建议”列必须包含以下值(顺序无关):
{", ".join(expected_fee_types)}
3. 仅返回匹配到的完整表格文本,不要附加任何解释或代码。
4. 若未匹配到,请返回空字符串。
""".strip()
messages = [{"role": "user", "content": prompt}]
# 3. 调用大模型
response = llm_client.chat.completions.create(
model=LLM_MODEL,
messages=messages,
temperature=0.0,
stream=False,
)
result = response.choices[0].message.content.strip()
logger.info("Successfully identified specific table", extra={"length": len(result)})
return result
# 使用示例
if __name__ == "__main__":
content = DocumentationExtractor.identify_specific_table(
file_path="/abs/path/云南电网有限责任公司安全文明施工费使用管理标准(试行).docx",
expected_columns=[
"费用类型及取费建议",
"措施类别及使用范围",
"措施类别编码",
"是否可摊销",
"变电",
"线路",
"配网",
],
expected_fee_types=["安全生产费", "文明施工费", "环境保护费"],
)
print(content)
修改分析
主要分为模块导入,...,对demo_class.py编写进行规范。
导入模块
核心要求是:标准库 → 第三方库 → 本地模块,各分组之间空一行,每组内部按字母序升序排列,绝对禁止 import *,class与导入模块相隔2行。
import logging
from typing import List
import pandas as pd
from docx import Document
from openai import OpenAI
from core.config import LLM_MODEL
from utils.logger import logger
注:不能一行导入多个包。
命名与可见性
主要对成员实例变量、方法、常量进行规范。前者为共有,后者为内部/私有。
- 实例变量:user_name,_user_name
- 方法:compute_total(),_compute_total()
- 常量:MAX_RETRY,_MAX_RETRY
类方法
类方法分为@staticmethod与@classmethod,前者可以理解为只是一个普通函数“借住”在类里,跟类没有任何绑定,无法访问类级别的任何信息。后者为类方法,可以访问类级别的任一信息,两者在使用时均可以用cls.method(),类名称直接点方法调用,实例方法与@classmethod属性类似,均可以访问类级别的任一信息,但是实例方法必须实例化,obj.method(),实例对象点方法调用。还有一个普通类属性,直接cls.arg调用,四者相互配合,下面以一个实际例子来展现它们之间的关系。
from __future__ import annotations
from dataclasses import dataclass, asdict
from decimal import Decimal
import datetime
@dataclass
class Order:
user_id: int
amount_cents: int # 以分为单位
status: str = "PENDING"
# ---------- 1. 实例方法:操作“这张订单” ----------
def pay(self) -> None:
if self.status != "PENDING":
raise ValueError("Only PENDING order can pay")
self.status = "PAID"
# 更新类级统计
Order._today_turnover_cents += self.amount_cents
def cancel(self) -> None:
self.status = "CANCELLED"
# ---------- 2. 类方法:工厂 + 统计 ----------
_today_turnover_cents: int = 0 # 类级属性:今日已付款总额
@classmethod
def from_json(cls, data: dict) -> Order:
"""反序列化 JSON -> Order 对象"""
return cls(
user_id=int(data["user_id"]),
amount_cents=int(data["amount_cents"]),
status=data.get("status", "PENDING")
)
@classmethod
def today_turnover_yuan(cls) -> Decimal:
"""取今日已付款总额(单位:元)"""
return Decimal(cls._today_turnover_cents) / 100
# ---------- 3. 静态方法:纯工具 ----------
@staticmethod
def cents_to_yuan(cents: int) -> Decimal:
"""分 → 元,与任何订单实例无关"""
return Decimal(cents) / 100
# ---------- 4. 普通类属性:回调 / 钩子 ----------
# 把“序列化方式”挂到类上,外部可替换(策略模式)
dump_fn = asdict
Order.dump_fn = o1
Q:什么时候选择实例方法,什么时候选择类方法?
A:当逻辑需要类级共享数据,例如,订单总数,选择类方法,否则均选择实例方法,主要有如下原因:面向对象的核心是“对象”、可扩展性更好、线程/并发更安全(类属性是全局共享的;实例属性隔离在线程各自的实例里,天然避免竞态条件。)
Q:普通类属性的作用?
A:可以对类级信息进行修改,以此通过外部替换策略模式。
cls注释规范
在class对应位置添加注释可使得cls有更好的可读性。
class Order:
"""单笔订单的业务对象.
负责状态机、金额计算与持久化。
Attributes:
order_id: 全局唯一订单号.
amount_cents: 订单金额(单位:分).
status: 订单状态,取值见 OrderStatus 枚举.
"""
def __init__(
self,
order_id: str,
items: List[str],
coupon: Optional[str] = None,
) -> None:
...
class DocumentationExtractor:
"""
从 Word 文档中提取并识别特定表格的通用工具类。
"""
@staticmethod
def _extract_all_tables(file_path: str) -> List[pd.DataFrame]:
"""
读取 Word 文档中的所有表格,并统一转换为 pandas.DataFrame 列表。
Args:
file_path (str): Word 文档绝对路径。
Returns:
List[pd.DataFrame]: 文档中所有表格对应的 DataFrame 列表。
"""
- 对于类,在正式编写之前要加入类的功能说明及公共属性说明;
- 对于类中的函数,参数要指明输入的类型,后面同时要指出输出的类型,整体格式为竖列,在正式编写之前要加入相关说明,说明主要包含三个部分,函数功能说明、参数说明、返回值说明。
提供__main__示例
自包含可运行的示例,Python 解释器启动后,会给每个模块(.py)自动设置一个全局变量__name__,如果该文件为主程序值为__main__,若该文件是被import,则值变为模块名。当文件为主程序时,运行函数下的代码,若不为主程序则忽略;
# 使用示例
if __name__ == "__main__":
content = DocumentationExtractor.identify_specific_table(
file_path="/abs/path/云南电网有限责任公司安全文明施工费使用管理标准(试行).docx",
expected_columns=[
"费用类型及取费建议",
"措施类别及使用范围",
"措施类别编码",
"是否可摊销",
"变电",
"线路",
"配网",
],
expected_fee_types=["安全生产费", "文明施工费", "环境保护费"],
)
print(content)

