如何在Python中实现类Go的点构建器语法并保留类型提示?
在Python中实现Go风格的点式构建语法(基于Pydantic模型)
期望的Go风格语法示例
package main import ( "encoding/json" "fmt" parasut "github.com/ozgur-yalcin/parasut.go/src" ) func main() { request := new(parasut.Request) request.SalesInvoice.Data.Attributes.ItemType = "invoice" // << 请勿修改! request.SalesInvoice.Data.Attributes.Description = "" // 发票标题 request.SalesInvoice.Data.Attributes.IssueDate = "" // ... request.SalesInvoice.Data.Relationships.Contact = new(parasut.SingleRelationShip) request.SalesInvoice.Data.Relationships.Contact.Data = new(parasut.RelationShip) request.SalesInvoice.Data.Relationships.Contact.Data.Type = "contacts" // << 请勿修改! request.SalesInvoice.Data.Relationships.Contact.Data.ID = "" // ... }
当前Python的嵌套实现方式
parasut.create_invoice( sales_invoice_form=ParasutCreateSalesInvoiceRequest( data=ParasutCreateSalesInvoiceRequestData( type="sales_invoices", attributes=ParasutSalesInvoiceCreateUpdateAttributes( net_total=1.00, item_type="invoice", description="", issue_date=datetime.date.today(), # ... ), relationships=ParasutCreateSalesInvoiceRequestDataRelationships( details=ParasutCreateSalesInvoiceRequestDataRelationshipsDetails( data=sales_invoice_details ), contact=ParasutCreatePurchaseBillBasicRequestDataRelationshipsSupplier( data=ParasutCreatePurchaseBillBasicRequestDataRelationshipsSupplierData( id=contact_id, type="contacts", ) ), ), ) ) )
需求与约束
- 用简洁的点式语法替代嵌套实例化,减少类导入量
- 保留Pyright的类型提示支持
- 基于OpenAPI Generator生成的Pydantic模型
可行实现方案
方案1:给Pydantic模型添加自动初始化嵌套对象的属性
通过自定义基类,利用__getattr__方法自动实例化未初始化的嵌套模型,同时保留类型提示。
from pydantic import BaseModel from typing import TypeVar T = TypeVar('T', bound=BaseModel) class AutoInitBaseModel(BaseModel): def __getattr__(self, name: str): field = self.__fields__.get(name) if field and issubclass(field.type_, BaseModel): nested_instance = field.type_() setattr(self, name, nested_instance) return nested_instance raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") # 修改OpenAPI Generator模板,让所有生成的模型继承AutoInitBaseModel class ParasutCreateSalesInvoiceRequest(AutoInitBaseModel): data: ParasutCreateSalesInvoiceRequestData | None = None # 使用方式 request = ParasutCreateSalesInvoiceRequest(type="sales_invoices") request.data.attributes.item_type = "invoice" request.data.attributes.description = "" request.data.relationships.contact.data.id = contact_id request.data.relationships.contact.data.type = "contacts" parasut.create_invoice(sales_invoice_form=request)
关键:修改OpenAPI Generator模板,批量让所有生成模型继承AutoInitBaseModel,无需手动修改每个类。
方案2:实现构建器类(Builder Pattern)
封装链式调用的构建器,内部维护Pydantic模型实例,最后输出完整模型。
class SalesInvoiceBuilder: def __init__(self): self.request = ParasutCreateSalesInvoiceRequest( data=ParasutCreateSalesInvoiceRequestData(type="sales_invoices") ) def set_item_type(self, item_type: str): self.request.data.attributes.item_type = item_type return self def set_description(self, description: str): self.request.data.attributes.description = description return self def set_contact(self, contact_id: str): self.request.data.relationships.contact = ParasutCreatePurchaseBillBasicRequestDataRelationshipsSupplier( data=ParasutCreatePurchaseBillBasicRequestDataRelationshipsSupplierData( id=contact_id, type="contacts" ) ) return self def build(self) -> ParasutCreateSalesInvoiceRequest: return self.request # 使用方式 request = SalesInvoiceBuilder()\ .set_item_type("invoice")\ .set_description("测试发票")\ .set_contact("123")\ .build() parasut.create_invoice(sales_invoice_form=request)
方案3:使用第三方库简化嵌套赋值
借助pydantic-assign这类库,通过路径字符串批量赋值,无需修改现有模型。
from pydantic_assign import assign request = ParasutCreateSalesInvoiceRequest(type="sales_invoices") assign(request, { "data.attributes.item_type": "invoice", "data.attributes.description": "", "data.relationships.contact.data.id": contact_id, "data.relationships.contact.data.type": "contacts" }) parasut.create_invoice(sales_invoice_form=request)
内容的提问来源于stack exchange,提问作者canersevdiceginibekleyenoglu
相关产品推荐
相关产品推荐

