Java 转 Python 后端 02: 面向对象到与数据建模体系
第 3 章 面向对象与数据建模体系3.1 类定义与构造classTool:registry:dict[str,Tool]{}# 类变量 ↔ static 字段所有实例共享def__init__(self,name:str,description:str):self.namename# 实例属性第一次赋值即创建无需先声明字段self.descriptiondescriptiondefrun(self,args:dict)-str:# 实例方法第一个参数永远是 selfreturnfrunning{self.name}toolTool(search,web search)# 没有 newtool.run({})Tool.registry# 类变量通过类访问与 Java 的区别__init__是初始化方法真正的构造器是__new__一般不碰。属性不需要提前声明self.xxx ...赋值即创建。这种灵活性也可能导致属性名拼写错误需依赖静态检查器pyright进行拦截。self必须显式写在参数第一位Java 的this是隐式的。3.2 没有真正的 privatePython 用下划线约定代替访问修饰符约定含义Java 对应name公开public_name内部使用约定解释器不阻止访问≈ 包私有/protected的弱化__name触发名称改写name mangling →_类名__name避免子类冲突≈private但也能硬访问__name__Python 保留的魔术方法名别这样给自己的属性命名—Python 没有编译期 interface 强制校验公开契约依赖命名约定与类型注解。配合 pyright 严格模式可以获得静态类型检查保障。3.3 propertyPython 不写 getter/setterJavaBean 风格的 getter/setter 样板代码在 Python 中通常不需要书写。直接公开属性即可当需要增加逻辑时使用property保持调用语法不变classConversation:def__init__(self,messages:list[dict]):self._messagesmessagespropertydeftoken_count(self)-int:# 调用方仍写 conv.token_count像属性一样returnsum(len(m[content])forminself._messages)propertydeflast_message(self)-dict:returnself._messages[-1]convConversation(history)conv.token_count# 注意没有括号不是方法调用Java 写getSomething()Python 直接访问.something——需要计算逻辑时再套property调用方代码不用改。3.4 继承、抽象类与接口的替代classBaseAgent:def__init__(self,name:str):self.namenamedefrun(self,task:str)-str:raiseNotImplementedError# ↔ abstract 方法defdescribe(self)-str:# 具体方法直接继承returnfAgent({self.name})classToolAgent(BaseAgent):def__init__(self,name:str,tools:list):super().__init__(name)# ↔ super(...)self.toolstoolsdefrun(self,task:str)-str:# Override 不存在直接同名覆盖...Python没有 interface替代方案按使用频率排序# 方案 1鸭子类型 —— 能跑就行不看类型看行为defexecute(runner):# 不声明类型只要 runner 有 .run() 方法returnrunner.run(task)# 方案 2Protocol结构化类型最接近 interface 的静态检查版fromtypingimportProtocolclassRunnable(Protocol):# 无需被显式 implements形状匹配即可defrun(self,task:str)-str:...defexecute(runner:Runnable)-str:...# 静态检查器会校验传入对象# 方案 3ABC显式抽象基类强制子类实现fromabcimportABC,abstractmethodclassBaseAgent(ABC):abstractmethoddefrun(self,task:str)-str:...agent 开发建议框架内部边界用 Protocol/ABC业务代码优先鸭子类型 注解。多继承Python 允许Java 类不行配合 MRO方法解析顺序解决菱形继承混入Mixin模式即基于多继承——LangGraph/DRF 等框架常见看懂即可。3.5 dataclass ↔ Lombokfromdataclassesimportdataclass,fielddataclassclassToolCall:# ↔ Lombok Data Builder AllArgsConstructorname:strarguments:dictfield(default_factorydict)# 可变默认值必须用 factorydefsummary(self)-str:# dataclass 可以正常加方法returnf{self.name}({self.arguments})callToolCall(namesearch,arguments{q:weather})print(call)# 自动生成 __repr__ / __eq__ / __hash__要点自动生成__init__、__repr__、__eq__。可变默认值陷阱args: dict {}会所有实例共享同一个 dict必须field(default_factorydict)。这是 Java 程序员最容易踩的 Python 坑之一。需要运行时校验/序列化JSON ↔ 对象时用pydantic 的BaseModel而不是 dataclass——见第 5.2 节。dataclass(frozenTrue)↔ Javarecord。3.6 typing 核心注解与泛型语法现代 Python3.9内置泛型语法不用再从typing导入List/Dictdefprocess(items:list[int],# ↔ ListIntegermapping:dict[str,list[int]],# ↔ MapString, ListIntegerpair:tuple[str,int],# 固定长度/类型的 tupletags:set[str],)-str|None:# ↔ OptionalString| 是 Union 语法糖...# Optional[X] 就是 X | NoneJava 里是 Nullabledeffind_user(uid:str)-User|None:...userfind_user(uid)ifuserisNone:# 调用方必须显式处理 Nonepyright 会强制...JavaPythonListStringlist[str]MapString, Integerdict[str, int]Nullable Stringstr | None即Optional[str]OptionalStringX | None 调用方检查没有 Optional 包装器泛型类class BoxTclass Box[T]:/Generic[T]? extends NumberSequence[Number]协变场景较少手写容器内的元素运行时不是真泛型没有 Java 的类型擦除问题因为根本不检查一切依赖静态检查器。工具链uvadd--devpyright# 或 mypyuv run pyright src/# CI 中执行提供类似编译期的静态类型保障建议在pyproject.toml开strict模式pyright从一开始就养成全注解习惯。3.7 Pydantic运行时真正生效的注解agent 开发核心dataclass 只在静态检查时有效。PydanticBaseModel把注解变成运行时校验 序列化等价于Jackson Bean Validation Lombok三合一是 FastAPI、所有 agent 框架结构化输出的基石frompydanticimportBaseModel,Field,field_validatorclassChatMessage(BaseModel):role:strField(descriptionsystem/user/assistant/tool 之一)content:strtokens:intField(default0,ge0)# ≥0 校验field_validator(role)classmethoddefcheck_role(cls,v:str)-str:ifvnotin{system,user,assistant,tool}:raiseValueError(finvalid role:{v})returnv msgChatMessage(roleuser,contenthi)# 自动 __init__无需 newmsg.model_dump()# → dict ↔ Jackson 序列化msg.model_dump_json()# → JSON 字符串ChatMessage.model_validate_json({role:user,content:hi})# ↔ Jackson 反序列化 校验ChatMessage(roleadmin,tokens-1)# 抛 ValidationError运行时强制嵌套模型自动解析LLM structured output 的标准形态classStep(BaseModel):thought:strtool:strarguments:dict[str,str]classPlan(BaseModel):goal:strsteps:list[Step]# 嵌套自动解析为 Step 对象max_turns:intField(default5,le20)planPlan.model_validate_json(llm_output_json)# 无效结构直接报错plan.steps[0].tool# 类型安全的属性访问在 Agent 后端项目中Pydantic 主要应用于以下三个核心场景工具参数 schema给 LLM 的 function calling 定义参数结构自动生成 JSON SchemaPlan.model_json_schema()。结构化输出让模型输出严格遵守定义OpenAIresponse_format、PydanticAI 的result_type。API 契约FastAPI 的请求/响应体。3.8 特殊方法魔术方法双下划线方法让自定义对象接入 Python 语法相当于 Java 的Comparable/Iterable/toString等接口classMessageHistory:def__init__(self):self._items:list[dict][]def__len__(self)-int:# 支持 len(obj)returnlen(self._items)def__iter__(self):# 支持 for-in↔ implements Iterablereturniter(self._items)def__getitem__(self,i:int)-dict:# 支持 obj[i]returnself._items[i]def__contains__(self,role:str)-bool:# 支持 inreturnany(m[role]roleforminself._items)def__str__(self)-str:# ↔ toString()returnf{len(self._items)}messagesdef__repr__(self)-str:# 调试时的详细表示...def__eq__(self,other)-bool:# ↔ equals()...def__call__(self,*args):# 让实例像函数一样可调用...常见对照toString()→__str__equals()/hashCode()→__eq__/__hash__Comparable→__lt__等Iterable/Iterator→__iter__/__next__with 语法 →__enter__/__exit__第 4.2 节。异常、生成器与装饰器在 Python 中属于核心语言特性与 Java 机制存在较多差异。3.9 枚举与类型别名fromenumimportEnum,StrEnumclassRole(StrEnum):# StrEnum枚举值就是 str序列化免转换SYSTEMsystemUSERuserASSISTANTassistantTOOLtoolRole.USER# Role.USER: userRole(user)# 按值查找Role.USER.value# user# 类型别名 —— 给复杂签名起名字提高可读性typeMessageslist[dict[str,str]]typeToolResultstr|dict|None其他常用类型标注工具类型检查时才生效fromtypingimportLiteral,TypedDict,NotRequiredclassChatMessage(TypedDict):# 带固定键的 dict 结构 ↔ recordrole:strcontent:strname:NotRequired[str]# 可选键defparse_mode(mode:Literal[fast,accurate])-str:...# Literal 受限取值比传裸字符串安全