You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 12:45:39