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

Python3.10中marshmallow_dataclass的NewType无法转为dataclass问题求助

问题描述

升级至Python 3.10.6并同步更新依赖到marshmallow_dataclass 8.5.8后,原本在Python 3.9+marshmallow_dataclass 8.3.0环境可正常运行的NewType示例代码报错,提示IPv4 is not a dataclass and cannot be turned into one。

原代码

from marshmallow_dataclass import NewType, dataclass, List
import marshmallow.validate
IPv4 = NewType('IPv4', str, validate=marshmallow.validate.Regexp(r'^([0-9]{1,3}\.){3}[0-9]{1,3}$'))

@dataclass
class MyIps:
   ips: List[IPv4]

MyIps.Schema().load({"ips": ["0.0.0.0", "grumble grumble"]})

报错信息

/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py:373: UserWarning: ****** WARNING ****** marshmallow_dataclass was called on the class <function NewType.<locals>.new_type at 0x7fb5a077fd90>, which is not a dataclass. It is going to try and convert the class into a dataclass, which may have undesirable side effects. To avoid this message, make sure all your classes and all the classes of their fields are either explicitly supported by marshmallow_dataclass, or define the schema explicitly using field(metadata=dict(marshmallow_field=...)). For more information, see https://github.com/lovasoa/marshmallow_dataclass/issues/51 ****** WARNING ******
warnings.warn(
Traceback (most recent call last):
File "/usr/lib/python3.10/dataclasses.py", line 1197, in fields
    fields = getattr(class_or_instance, _FIELDS)
AttributeError: 'function' object has no attribute '__dataclass_fields__'. Did you mean: '__dataclass_params__'?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 370, in _internal_class_schema
    fields: Tuple[dataclasses.Field, ...] = dataclasses.fields(clazz)
File "/usr/lib/python3.10/dataclasses.py", line 1199, in fields
    raise TypeError('must be called with a dataclass type or instance')
TypeError: must be called with a dataclass type or instance

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 384, in _internal_class_schema
    created_dataclass: type = dataclasses.dataclass(clazz)
File "/usr/lib/python3.10/dataclasses.py", line 1185, in dataclass
    return wrap(cls)
File "/usr/lib/python3.10/dataclasses.py", line 1176, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash,
File "/usr/lib/python3.10/dataclasses.py", line 909, in _process_class
    for b in cls.__mro__[-1:0:-1]:
AttributeError: 'function' object has no attribute '__mro__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/username/testscript.python", line 9, in <module>
    MyIps.Schema().load({"ips": ["0.0.0.0", "grumble grumble"]})
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/lazy_class_attribute.py", line 33, in __get__
    setattr(cls, self.name, self.func())
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 356, in class_schema
    return _internal_class_schema(clazz, base_schema, clazz_frame)
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 402, in _internal_class_schema
    attributes.update(
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 405, in <genexpr>
    field_for_schema(
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 695, in field_for_schema
    generic_field = _field_for_generic_type(typ, base_schema, typ_frame, **metadata)
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 503, in _field_for_generic_type
    child_type = field_for_schema(
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 729, in field_for_schema
    or _internal_class_schema(typ, base_schema, typ_frame)
File "/home/username/.local/share/virtualenvs/venv_3.10/lib/python3.10/site-packages/marshmallow_dataclass/__init__.py", line 387, in _internal_class_schema
    raise TypeError(
TypeError: IPv4 is not a dataclass and cannot be turned into one.
问题原因

marshmallow_dataclass 8.4.0及后续版本调整了类型解析逻辑,不再支持marshmallow_dataclass.NewType直接携带validate参数的用法。新版本中,该方式生成的IPv4会被识别为普通函数类型,触发将其转为数据类的无效尝试,最终导致报错。这属于依赖包版本迭代中的API行为变更,与Python 3.10本身无关。

解决方法(无需降级)

提供三种可行的替代方案:

方案1:用dataclasses.field搭配metadata指定验证规则

直接在数据类字段中定义验证逻辑,替代原NewType的用法:

from dataclasses import field
from marshmallow_dataclass import dataclass, List
import marshmallow.validate

@dataclass
class MyIps:
    ips: List[str] = field(
        metadata={
            "marshmallow_field": marshmallow.fields.List(
                marshmallow.fields.String(
                    validate=marshmallow.validate.Regexp(r'^([0-9]{1,3}\.){3}[0-9]{1,3}$')
                )
            )
        }
    )

MyIps.Schema().load({"ips": ["0.0.0.0", "grumble grumble"]})

方案2:自定义marshmallow字段类型

先定义带验证规则的专属字段,再在数据类中引用:

from dataclasses import field
from marshmallow_dataclass import dataclass, List
import marshmallow
import marshmallow.validate

# 自定义IPv4验证字段
IPv4Field = marshmallow.fields.String(validate=marshmallow.validate.Regexp(r'^([0-9]{1,3}\.){3}[0-9]{1,3}$'))

@dataclass
class MyIps:
    ips: List[str] = field(metadata={"marshmallow_field": marshmallow.fields.List(IPv4Field)})

MyIps.Schema().load({"ips": ["0.0.0.0", "grumble grumble"]})

方案3:结合标准库typing.NewType与字段验证

若需保留类型提示,可使用标准库NewType定义类型,再通过字段验证实现规则:

from typing import NewType
from dataclasses import field
from marshmallow_dataclass import dataclass, List
import marshmallow.validate

# 仅用标准库NewType做类型提示
IPv4 = NewType('IPv4', str)

@dataclass
class MyIps:
    ips: List[IPv4] = field(
        metadata={
            "marshmallow_field": marshmallow.fields.List(
                marshmallow.fields.String(
                    validate=marshmallow.validate.Regexp(r'^([0-9]{1,3}\.){3}[0-9]{1,3}$')
                )
            )
        }
    )

MyIps.Schema().load({"ips": ["0.0.0.0", "grumble grumble"]})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 05:25:37