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

如何在Pydantic中对GeoJSON对象的坐标进行6位小数取整?

GeoJSON Polygon坐标保留6位小数的Pydantic实现问题

需求

使用Python 3.10.6,需将GeoJSON的Polygon坐标(CustomCoordinates中的纬度lat和经度lon)保留6位小数,要求不通过循环遍历,而是在基础层级处理。已导入pydantic_geojson的内置几何类,需修改坐标实现6位小数取整。

尝试过的方案及问题

方案1:给LonField添加decimal_places=6约束

尝试在Field中设置decimal_places=6:

LonField = Annotated[
    Union[float, float],
    Field(
        title='Coordinate longitude',
        decimal_places=6,
        gt=-180,
        lt=180,
    )
]

触发报错:

File "pydantic\main.py", line 198, in pydantic.main.ModelMetaclass.__new__
  File "pydantic\fields.py", line 506, in pydantic.fields.ModelField.infer
  File "pydantic\fields.py", line 436, in pydantic.fields.ModelField.__init__
  File "pydantic\fields.py", line 552, in pydantic.fields.ModelField.prepare
  File "pydantic\fields.py", line 661, in pydantic.fields.ModelField._type_analysis
  File "pydantic\fields.py", line 758, in pydantic.fields.ModelField._type_analysis
  File "pydantic\fields.py", line 808, in pydantic.fields.ModelField._create_sub_type
  File "pydantic\fields.py", line 436, in pydantic.fields.ModelField.__init__
  File "pydantic\fields.py", line 552, in pydantic.fields.ModelField.prepare
  File "pydantic\fields.py", line 758, in pydantic.fields.ModelField._type_analysis
  File "pydantic\fields.py", line 808, in pydantic.fields.ModelField._create_sub_type
  File "pydantic\fields.py", line 436, in pydantic.fields.ModelField.__init__
  File "pydantic\fields.py", line 557, in pydantic.fields.ModelField.prepare
  File "pydantic\fields.py", line 831, in pydantic.fields.ModelField.populate_validators
  File "pydantic\validators.py", line 735, in find_validators
  File "pydantic\validators.py", line 599, in pydantic.validators.make_namedtuple_validator
  File "pydantic\annotated_types.py", line 72, in pydantic.annotated_types.create_model_from_namedtuple
  File "pydantic\main.py", line 1026, in pydantic.main.create_model
  File "pydantic\main.py", line 198, in pydantic.main.ModelMetaclass.__new__
  File "pydantic\fields.py", line 504, in pydantic.fields.ModelField.infer
  File "pydantic\schema.py", line 1011, in pydantic.schema.get_annotation_from_field_info
ValueError: On field "lon" the following field constraints are set but not enforced: decimal_places. 

方案2:使用验证器但未生效

尝试给CustomCoordinates添加验证器,但未实现取整效果:

class CustomCoordinates(NamedTuple):
    lat: LatField
    lon: LonField

    @validator('lat')
    def coordinates_roundOff(cls,v):
        return round(v, 6)

当前相关代码

LonField = Annotated[
    Union[float, float],
    Field(
        title='Coordinate longitude',
        gt=-180,
        lt=180,
    )
]

LatField = Annotated[
    Union[float, float],
    Field(
        title='Coordinate latitude',
        gt=-90,
        lt=90,
    ),
]


class CustomCoordinates(NamedTuple):
    lat: LatField
    lon: LonField


class PolygonGeo(CamelModel):
    type: Optional[str] = Field(None, description='Type of GeoJSon geometry', example='Polygon')
    coordinates: Optional[List[List['CustomCoordinates']]] = Field(
        None, description='Coordinates of GeoJSon geometry', example='[[[-73.7786925003853,40.63992777725],...]]]')

解决方案

核心原因

  1. decimal_places约束仅对Decimal类型生效,无法作用于float类型,直接添加会触发未生效约束报错。
  2. 标准库NamedTuple不支持Pydantic的验证器逻辑,导致添加的验证器无法触发。

实现方法

方法1:自定义带取整逻辑的字段类型

将取整逻辑封装到自定义字段类型中,所有使用该类型的字段会自动完成6位小数取整:

from pydantic import GetCoreSchemaHandler, Field, Annotated
from pydantic_core import core_schema
from pydantic import CamelModel, BaseModel
from typing import Optional, List

# 自定义带6位小数取整的float类型
class RoundedFloat(float):
    @classmethod
    def __get_pydantic_core_schema__(cls, source_type, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
        return core_schema.no_info_wrap_validator_function(
            cls.validate,
            handler(float),
        )
    
    @classmethod
    def validate(cls, v: float) -> float:
        return round(v, 6)

# 定义经纬度字段,继承自定义类型并添加范围约束
LonField = Annotated[
    RoundedFloat,
    Field(
        title='Coordinate longitude',
        gt=-180,
        lt=180,
    )
]

LatField = Annotated[
    RoundedFloat,
    Field(
        title='Coordinate latitude',
        gt=-90,
        lt=90,
    ),
]

# 改用Pydantic BaseModel替代NamedTuple,确保验证逻辑生效
class CustomCoordinates(BaseModel):
    lat: LatField
    lon: LonField

class PolygonGeo(CamelModel):
    type: Optional[str] = Field(None, description='GeoJSON几何类型', example='Polygon')
    coordinates: Optional[List[List[CustomCoordinates]]] = Field(
        None, description='GeoJSON几何坐标', example='[[[-73.7786925003853,40.63992777725],...]]]')

方法2:给BaseModel添加字段验证器

如果偏好使用验证器的方式,可改用BaseModel并添加字段级验证器:

from pydantic import field_validator

class CustomCoordinates(BaseModel):
    lat: LatField
    lon: LonField

    @field_validator('lat', 'lon')
    def round_coordinates(cls, v: float) -> float:
        return round(v, 6)

# PolygonGeo定义保持不变

说明

  • 改用BaseModel替代NamedTuple是因为Pydantic对BaseModel的验证逻辑支持更完善,能确保在字段解析的基础层级自动处理坐标取整,无需手动遍历整个Polygon坐标数组。
  • 自定义RoundedFloat类型的方式更符合“基础层级处理”的需求,将取整逻辑与字段类型绑定,复用性更强。

内容的提问来源于stack exchange,提问作者Darshan Patil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:50:27