Pythonic多态参数处理:Blender不同几何类型创建函数问询
嘿,这个场景我太熟了!在Blender里搞几何生成的时候,这种不同类型需要不同参数的情况确实很常见,而且你已经想到用枚举了,这思路很棒~
首先先回应你提到的字典封装参数:这确实是个可行的基础方案,但它的问题是没有类型提示和参数校验——IDE没法帮你补全参数,很容易传错键名或者参数类型。下面给你几个更Pythonic、更易维护的方案:
1. 类型安全首选:TypedDict + Match-Case(Python 3.10+)
这个方案结合了类型提示和清晰的分支逻辑,既保留了单函数的简洁,又能避免参数错误。
步骤1:定义枚举和参数类型
先用Enum定义几何类型,再用TypedDict给每种几何类型定义专属的参数结构(IDE会自动识别这些类型,给你补全提示):
from enum import Enum import bpy from typing import TypedDict, Union # 几何类型枚举 class GeometryType(Enum): ICOSPHERE = "icosphere" UV_SPHERE = "uv_sphere" CUBE = "cube" # 每种几何的参数结构 class IcoSphereParams(TypedDict): subdivisions: int # 必填参数 radius: float # 可选参数(可以加默认值) class UvSphereParams(TypedDict): segments: int rings: int radius: float class CubeParams(TypedDict): size: float # 统一参数类型 GeometryParams = Union[IcoSphereParams, UvSphereParams, CubeParams]
步骤2:编写单函数处理逻辑
用Python 3.10+的match-case语法替代冗长的if-elif,代码更清晰:
def create_geometry(geo_type: GeometryType, params: GeometryParams): # 先清空选中物体,避免干扰创建 bpy.ops.object.select_all(action='DESELECT') match geo_type: case GeometryType.ICOSPHERE: bpy.ops.mesh.primitive_ico_sphere_add( subdivisions=params["subdivisions"], radius=params.get("radius", 1.0), # 给可选参数加默认值 enter_editmode=False ) case GeometryType.UV_SPHERE: bpy.ops.mesh.primitive_uv_sphere_add( segments=params["segments"], rings=params["rings"], radius=params.get("radius", 1.0), enter_editmode=False ) case GeometryType.CUBE: bpy.ops.mesh.primitive_cube_add( size=params.get("size", 2.0), enter_editmode=False ) case _: raise ValueError(f"不支持的几何类型:{geo_type}")
调用示例
# 创建细分3级、半径1.5的二十面体 create_geometry(GeometryType.ICOSPHERE, {"subdivisions": 3, "radius": 1.5}) # 创建32段16环的UV球 create_geometry(GeometryType.UV_SPHERE, {"segments": 32, "rings": 16}) # 创建大小为3的立方体 create_geometry(GeometryType.CUBE, {"size": 3.0})
这个方案的优点:
- IDE会自动提示每种几何类型需要的参数,避免拼写错误
- 分支逻辑清晰,新增几何类型只需加枚举、TypedDict和match分支
- 可选参数可以通过
get()设置默认值,灵活度高
2. 进阶解耦:策略模式绑定枚举
如果你的项目后续要加很多几何类型,这个方案会更优雅——把每种几何的创建逻辑直接绑定到枚举值上,create_geometry函数完全不用修改,符合开闭原则。
实现代码
from enum import Enum import bpy from typing import Callable, TypedDict from pydantic import TypeAdapter # 可选,用于严格参数校验 # 定义参数类型 class IcoSphereParams(TypedDict): subdivisions: int radius: float class UvSphereParams(TypedDict): segments: int rings: int radius: float class CubeParams(TypedDict): size: float # 枚举直接绑定创建函数和参数类型 class GeometryType(Enum): ICOSPHERE = ( "icosphere", lambda params: bpy.ops.mesh.primitive_ico_sphere_add( subdivisions=params["subdivisions"], radius=params.get("radius", 1.0), enter_editmode=False ), IcoSphereParams ) UV_SPHERE = ( "uv_sphere", lambda params: bpy.ops.mesh.primitive_uv_sphere_add( segments=params["segments"], rings=params["rings"], radius=params.get("radius", 1.0), enter_editmode=False ), UvSphereParams ) CUBE = ( "cube", lambda params: bpy.ops.mesh.primitive_cube_add( size=params.get("size", 2.0), enter_editmode=False ), CubeParams ) # 枚举初始化方法,绑定属性 def __init__(self, name: str, creator: Callable[[dict], None], param_type: type): self.name = name self.creator = creator self.param_type = param_type def create_geometry(geo_type: GeometryType, params: dict): # 可选:用pydantic做严格参数校验,避免传错参数 try: TypeAdapter(geo_type.param_type).validate_python(params) except Exception as e: raise ValueError(f"{geo_type.name}参数错误:{str(e)}") bpy.ops.object.select_all(action='DESELECT') # 直接调用枚举绑定的创建函数 geo_type.creator(params)
这个方案的核心是把每个几何类型的创建逻辑当成“策略”存在枚举里,后续新增几何类型时,只需要在GeometryType里加一项,完全不用改动create_geometry函数,代码耦合度极低。
总结
- 如果你只是快速实现功能,字典参数方案足够用,但缺乏类型安全
- 追求类型提示和代码清晰,选
TypedDict + Match-Case - 项目需要扩展性,选枚举绑定策略的方案
内容的提问来源于stack exchange,提问作者slerfsterdn
相关产品推荐
相关产品推荐

