# Schemas DataClass
[](https://pypi.org/project/schemas-dataclass/)
[](LICENSE)
[](https://github.com/b40yd/schemas-python/actions/workflows/ci.yml)
[](#)
一个专为 Python 2/3 兼容设计的 DataClass 库,支持完整的数据校验功能、装饰器语法和自定义错误消息。
## 🚀 快速开始
### 安装
```bash
pip install schemas-dataclass
```
### 基础使用
```python
from schemas_dataclass import StringField, NumberField, dataclass, ValidationError
@dataclass
class User(object):
name = StringField(min_length=2, max_length=50)
age = NumberField(minvalue=0, maxvalue=120)
email = StringField(
regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
)
# 创建用户
user = User(name="Alice", age=25, email="alice@example.com")
print(user.to_dict()) # {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}
```
### 自定义错误消息
```python
@dataclass
class User(object):
name = StringField(
min_length=2,
error_messages={
'required': '姓名是必填项',
'min_length': '姓名至少需要 {min_length} 个字符'
}
)
try:
user = User(name="A") # 太短
except ValidationError as e:
print(e.message) # 输出: 姓名至少需要 2 个字符
```
## 📚 文档索引
- **[安装和使用](#安装和使用)** - 快速上手指南
- **[完整示例](#完整示例)** - 丰富的使用示例
- **[API 参考](#api-参考)** - 详细的 API 文档
- **[测试](#测试)** - 如何运行测试
- **[项目结构说明](PROJECT_STRUCTURE.md)** - 项目文件结构和开发指南
- **[更新日志](CHANGELOG.md)** - 版本更新记录和功能变更
- **[自定义错误消息详细文档](CUSTOM_ERROR_MESSAGES.md)** - 完整的自定义错误消息使用指南
- **[贡献指南](#贡献指南)** - 如何参与项目开发
## 安装和使用
### 安装方式
#### 从 PyPI 安装(推荐)
```bash
pip install schemas-dataclass
```
#### 从源码安装
```bash
git clone https://github.com/schemas/dataclass.git
cd dataclass
python setup.py install
```
#### 开发环境安装
```bash
git clone https://github.com/schemas/dataclass.git
cd dataclass
pip install -e .
pip install -r requirements-dev.txt
```
### 基础使用指南
```python
from schemas_dataclass import StringField, NumberField, ListField, dataclass
@dataclass
class User(object):
name = StringField(min_length=2, max_length=50)
age = NumberField(minvalue=0, maxvalue=120)
email = StringField(
regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
)
tags = ListField(item_type=str, required=False)
# 创建和使用
user = User(
name="Alice",
age=25,
email="alice@example.com",
tags=["developer", "python"]
)
print(user.name) # Alice
print(user['age']) # 25
print(user.get('email')) # alice@example.com
print(user.to_dict()) # 转换为字典
```
## 核心特性
### 🔧 字段类型支持
- **StringField**: 字符串字段
- 长度验证 (`min_length`, `max_length`)
- 正则表达式验证 (`regex`)
- 枚举验证 (`choices`)
- 自定义错误消息支持
- **NumberField**: 数字字段(支持int、float、long)
- 范围验证 (`minvalue`, `maxvalue`)
- 枚举验证 (`choices`)
- 自定义错误消息支持
- **ListField**: 数组字段
- 长度验证 (`min_length`, `max_length`)
- 支持嵌套类型验证 (`item_type`)
- 支持字符串、数字、dataclass模型嵌套
- 自定义错误消息支持
### 🌍 自定义错误消息
- **多语言支持**: 支持中文、英文等多语言错误消息
- **模板格式化**: 支持 `{参数名}` 格式的参数替换
- **完整覆盖**: 支持所有验证类型的自定义错误消息
- **向后兼容**: 不影响现有代码,可选使用
```python
# 自定义错误消息示例
@dataclass
class User(object):
name = StringField(
min_length=3,
max_length=20,
error_messages={
'required': '用户名是必填项',
'min_length': '用户名至少需要 {min_length} 个字符',
'max_length': '用户名不能超过 {max_length} 个字符'
}
)
```
### 🎯 装饰器语法
```python
@dataclass
class User(object):
name = StringField(min_length=1, max_length=100)
age = NumberField(minvalue=0, maxvalue=150)
```
### 🔍 自定义验证装饰器
```python
@dataclass
class Product(object):
name = StringField()
price = NumberField()
@validate("name")
def validate_name_custom(self, name):
if not name.isalnum():
raise ValidationError("Name must be alphanumeric")
@validate("price")
def validate_price_custom(self, price):
if price <= 0:
raise ValidationError("Price must be positive")
```
### 🔧 自定义get方法
```python
@dataclass
class BlogPost(object):
title = StringField()
status = StringField(default='draft')
def get_title(self):
"""自定义获取标题的方法"""
title = self.__dict__.get('title', '')
status = self.__dict__.get('status', 'draft')
return "[{0}] {1}".format(status.upper(), title)
```
## 完整示例
### 📁 示例文件
项目提供了丰富的示例文件,位于 `examples/` 目录:
- **[基础使用示例](examples/basic_usage.py)** - 字段类型、dataclass 基础功能
- **[自定义错误消息示例](examples/custom_error_messages.py)** - 多语言错误消息、模板格式化
- **[高级功能示例](examples/advanced_features.py)** - 自定义验证、嵌套 dataclass、条件验证
- **[实际应用示例](examples/real_world_examples.py)** - 用户管理、电商产品、博客系统
### 🚀 运行示例
```bash
# 基础使用示例
python examples/basic_usage.py
# 自定义错误消息示例
python examples/custom_error_messages.py
# 高级功能示例
python examples/advanced_features.py
# 实际应用示例
python examples/real_world_examples.py
```
### 💡 快速示例
#### 用户管理系统
```python
from schemas_dataclass import StringField, NumberField, ListField, dataclass, validate
@dataclass
class User(object):
username = StringField(
min_length=3,
max_length=20,
regex=r'^[a-zA-Z][a-zA-Z0-9_]*$',
error_messages={
'required': '用户名是必填项',
'min_length': '用户名至少需要 {min_length} 个字符',
'regex': '用户名必须以字母开头,只能包含字母、数字和下划线'
}
)
email = StringField(
regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
error_messages={
'required': '邮箱地址是必填项',
'regex': '请输入有效的邮箱地址'
}
)
age = NumberField(
minvalue=13,
maxvalue=120,
error_messages={
'minvalue': '年龄不能小于 {minvalue} 岁',
'maxvalue': '年龄不能大于 {maxvalue} 岁'
}
)
tags = ListField(
item_type=str,
required=False,
max_length=10,
error_messages={
'max_length': '标签数量不能超过 {max_length} 个'
}
)
@validate("username")
def validate_username_not_reserved(self, username):
"""检查用户名是否为保留词"""
reserved = ['admin', 'root', 'system']
if username.lower() in reserved:
raise ValidationError(f"用户名 '{username}' 是系统保留词")
# 使用示例
user = User(
username="alice_dev",
email="alice@example.com",
age=28,
tags=["developer", "python"]
)
print("用户: {}".format(user.username))
print("邮箱: {}".format(user.email))
print("年龄: {}".format(user.age))
print("标签: {}".format(user.tags))
```
## API 参考
> **重要变更说明**: 从版本 2.0 开始,所有字段默认为可选 (`required=False`)。如需必填字段,请显式设置 `required=True`。
### 字段类型
#### StringField
```python
StringField(
default=None, # 默认值
alias=None, # 字段别名
required=False, # 是否必填 (默认为 False)
min_length=None, # 最小长度
max_length=None, # 最大长度
regex=None, # 正则表达式
choices=None, # 枚举选项
error_messages=None # 自定义错误消息
)
```
#### NumberField
```python
NumberField(
default=None, # 默认值
alias=None, # 字段别名
required=False, # 是否必填 (默认为 False)
minvalue=None, # 最小值
maxvalue=None, # 最大值
choices=None, # 枚举选项
error_messages=None # 自定义错误消息
)
```
#### ListField
```python
ListField(
default=None, # 默认值
alias=None, # 字段别名
required=False, # 是否必填 (默认为 False)
min_length=None, # 最小长度
max_length=None, # 最大长度
item_type=None, # 列表项类型
error_messages=None # 自定义错误消息
)
```
### 装饰器
#### @dataclass
```python
@dataclass
class MyClass(object):
field1 = StringField()
field2 = NumberField()
```
#### @validate
```python
@dataclass
class MyClass(object):
field1 = StringField()
@validate("field1")
def validate_field1(self, value):
# 自定义验证逻辑
if not condition:
raise ValidationError("Custom validation failed")
```
### 错误消息键
#### 通用错误消息键
- `required`: 必填字段为空
- `invalid_type`: 类型不匹配
#### StringField 错误消息键
- `min_length`: 长度小于最小值
- `max_length`: 长度大于最大值
- `regex`: 正则表达式匹配失败
- `choices`: 值不在枚举选项中
#### NumberField 错误消息键
- `minvalue`: 数值小于最小值
- `maxvalue`: 数值大于最大值
- `choices`: 值不在枚举选项中
#### ListField 错误消息键
- `min_length`: 列表长度小于最小值
- `max_length`: 列表长度大于最大值
- `invalid_list_item`: 列表项类型不匹配
## 测试
### 运行测试
```bash
# 运行所有测试
pytest
# 运行特定测试文件
pytest tests/test_fields.py
# 运行带覆盖率的测试
pytest --cov=schemas_dataclass
# 运行特定标记的测试
pytest -m "unit"
pytest -m "integration"
pytest -m "error_messages"
```
### 测试结构
```
tests/
├── conftest.py # pytest 配置和 fixtures
├── test_fields.py # 字段类型测试
├── test_custom_error_messages.py # 自定义错误消息测试
├── test_dataclass.py # dataclass 功能测试
└── test_integration.py # 集成测试
```
### 测试覆盖
- **25+ 个测试用例**,覆盖所有功能点
- **100% 测试通过率**
- **向后兼容性验证**
- **多语言错误消息测试**
- **复杂场景边界测试**
## 验证特性
### 字符串验证
- 长度验证:`min_length`, `max_length`
- 正则表达式验证:`regex`
- 枚举验证:`choices`
- 自定义错误消息:支持所有验证类型
### 数字验证
- 范围验证:`minvalue`, `maxvalue`
- 枚举验证:`choices`
- 类型验证:自动支持int、float、long(Python 2)
- 自定义错误消息:支持所有验证类型
### 数组验证
- 长度验证:`min_length`, `max_length`
- 项类型验证:`item_type`
- 支持嵌套:字符串、数字、dataclass模型
- 自定义错误消息:支持列表项类型错误
### DataClass字段支持
- 支持dataclass作为字段类型
- 自动实例化和验证
- 重新赋值时重新创建对象
- 支持嵌套的to_dict()转换
### 自定义验证
- 使用`@validate("field_name")`装饰器
- 在基础验证之后执行
- 支持多个自定义验证函数
### 自定义错误消息特性
- **多语言支持**:完全支持中文、英文等多语言错误消息
- **模板格式化**:支持 `{参数名}` 格式的参数替换,如 `{min_length}`, `{maxvalue}` 等
- **完整覆盖**:支持所有验证类型的自定义错误消息
- **向后兼容**:不影响现有代码,可选使用
- **健壮性**:格式化失败时优雅降级,返回原始模板
- **零性能影响**:不使用自定义消息时性能与原版本完全相同
#### 支持的错误消息类型
- **通用**: `required`, `invalid_type`
- **StringField**: `min_length`, `max_length`, `regex`, `choices`
- **NumberField**: `minvalue`, `maxvalue`, `choices`
- **ListField**: `min_length`, `max_length`, `invalid_list_item`
## 兼容性
- **Python 2.7+**: 完全支持
- **Python 3.4+**: 完全支持
- **PyPy**: 支持
- **Jython**: 理论支持(未测试)
## 性能
- **零依赖**: 仅使用 Python 标准库
- **轻量级**: 核心代码不到 1000 行
- **高性能**: 验证速度快,内存占用低
- **可扩展**: 易于添加新的字段类型和验证规则
## 贡献指南
欢迎贡献代码!请遵循以下步骤:
1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
3. 添加测试用例
4. 确保所有测试通过 (`pytest`)
5. 更新相关文档
6. 提交更改 (`git commit -m 'Add amazing feature'`)
7. 推送到分支 (`git push origin feature/amazing-feature`)
8. 创建 Pull Request
### 开发环境设置
```bash
git clone https://github.com/schemas/dataclass.git
cd dataclass
pip install -e .
pip install -r requirements-dev.txt
```
### 代码规范
- 遵循 PEP 8 代码风格
- 添加适当的文档字符串
- 为新功能添加测试用例
- 保持 Python 2/3 兼容性
## 许可证
本项目采用 GNU General Public License v3.0 许可证。详见 [LICENSE](LICENSE) 文件。
## 更新日志
查看 [CHANGELOG.md](CHANGELOG.md) 了解详细的版本更新记录。
---
**注意**: 本库完全兼容 Python 2.7 和 Python 3.x,自定义错误消息功能为可选特性,不影响现有代码的使用。
Raw data
{
"_id": null,
"home_page": null,
"name": "schemas-dataclass",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Schemas DataClass Team <bb.qnyd@gmail.com>",
"keywords": "dataclass, fields, python2, python3, schema, validation",
"author": null,
"author_email": "Schemas DataClass Team <bb.qnyd@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/46/87/780af591c6fb0ce9420705ae2e3860833efb4feeecb97533113a03508eb5/schemas_dataclass-0.0.1.tar.gz",
"platform": null,
"description": "# Schemas DataClass\n\n[](https://pypi.org/project/schemas-dataclass/)\n[](LICENSE)\n[](https://github.com/b40yd/schemas-python/actions/workflows/ci.yml)\n[](#)\n\n\u4e00\u4e2a\u4e13\u4e3a Python 2/3 \u517c\u5bb9\u8bbe\u8ba1\u7684 DataClass \u5e93\uff0c\u652f\u6301\u5b8c\u6574\u7684\u6570\u636e\u6821\u9a8c\u529f\u80fd\u3001\u88c5\u9970\u5668\u8bed\u6cd5\u548c\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u3002\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n### \u5b89\u88c5\n\n```bash\npip install schemas-dataclass\n```\n\n### \u57fa\u7840\u4f7f\u7528\n\n```python\nfrom schemas_dataclass import StringField, NumberField, dataclass, ValidationError\n\n@dataclass\nclass User(object):\n name = StringField(min_length=2, max_length=50)\n age = NumberField(minvalue=0, maxvalue=120)\n email = StringField(\n regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'\n )\n\n# \u521b\u5efa\u7528\u6237\nuser = User(name=\"Alice\", age=25, email=\"alice@example.com\")\nprint(user.to_dict()) # {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}\n```\n\n### \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n\n```python\n@dataclass\nclass User(object):\n name = StringField(\n min_length=2,\n error_messages={\n 'required': '\u59d3\u540d\u662f\u5fc5\u586b\u9879',\n 'min_length': '\u59d3\u540d\u81f3\u5c11\u9700\u8981 {min_length} \u4e2a\u5b57\u7b26'\n }\n )\n\ntry:\n user = User(name=\"A\") # \u592a\u77ed\nexcept ValidationError as e:\n print(e.message) # \u8f93\u51fa: \u59d3\u540d\u81f3\u5c11\u9700\u8981 2 \u4e2a\u5b57\u7b26\n```\n\n## \ud83d\udcda \u6587\u6863\u7d22\u5f15\n\n- **[\u5b89\u88c5\u548c\u4f7f\u7528](#\u5b89\u88c5\u548c\u4f7f\u7528)** - \u5feb\u901f\u4e0a\u624b\u6307\u5357\n- **[\u5b8c\u6574\u793a\u4f8b](#\u5b8c\u6574\u793a\u4f8b)** - \u4e30\u5bcc\u7684\u4f7f\u7528\u793a\u4f8b\n- **[API \u53c2\u8003](#api-\u53c2\u8003)** - \u8be6\u7ec6\u7684 API \u6587\u6863\n- **[\u6d4b\u8bd5](#\u6d4b\u8bd5)** - \u5982\u4f55\u8fd0\u884c\u6d4b\u8bd5\n- **[\u9879\u76ee\u7ed3\u6784\u8bf4\u660e](PROJECT_STRUCTURE.md)** - \u9879\u76ee\u6587\u4ef6\u7ed3\u6784\u548c\u5f00\u53d1\u6307\u5357\n- **[\u66f4\u65b0\u65e5\u5fd7](CHANGELOG.md)** - \u7248\u672c\u66f4\u65b0\u8bb0\u5f55\u548c\u529f\u80fd\u53d8\u66f4\n- **[\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u8be6\u7ec6\u6587\u6863](CUSTOM_ERROR_MESSAGES.md)** - \u5b8c\u6574\u7684\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u4f7f\u7528\u6307\u5357\n- **[\u8d21\u732e\u6307\u5357](#\u8d21\u732e\u6307\u5357)** - \u5982\u4f55\u53c2\u4e0e\u9879\u76ee\u5f00\u53d1\n\n## \u5b89\u88c5\u548c\u4f7f\u7528\n\n### \u5b89\u88c5\u65b9\u5f0f\n\n#### \u4ece PyPI \u5b89\u88c5\uff08\u63a8\u8350\uff09\n\n```bash\npip install schemas-dataclass\n```\n\n#### \u4ece\u6e90\u7801\u5b89\u88c5\n\n```bash\ngit clone https://github.com/schemas/dataclass.git\ncd dataclass\npython setup.py install\n```\n\n#### \u5f00\u53d1\u73af\u5883\u5b89\u88c5\n\n```bash\ngit clone https://github.com/schemas/dataclass.git\ncd dataclass\npip install -e .\npip install -r requirements-dev.txt\n```\n\n### \u57fa\u7840\u4f7f\u7528\u6307\u5357\n\n```python\nfrom schemas_dataclass import StringField, NumberField, ListField, dataclass\n\n@dataclass\nclass User(object):\n name = StringField(min_length=2, max_length=50)\n age = NumberField(minvalue=0, maxvalue=120)\n email = StringField(\n regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'\n )\n tags = ListField(item_type=str, required=False)\n\n# \u521b\u5efa\u548c\u4f7f\u7528\nuser = User(\n name=\"Alice\",\n age=25,\n email=\"alice@example.com\",\n tags=[\"developer\", \"python\"]\n)\n\nprint(user.name) # Alice\nprint(user['age']) # 25\nprint(user.get('email')) # alice@example.com\nprint(user.to_dict()) # \u8f6c\u6362\u4e3a\u5b57\u5178\n```\n\n## \u6838\u5fc3\u7279\u6027\n\n### \ud83d\udd27 \u5b57\u6bb5\u7c7b\u578b\u652f\u6301\n\n- **StringField**: \u5b57\u7b26\u4e32\u5b57\u6bb5\n - \u957f\u5ea6\u9a8c\u8bc1 (`min_length`, `max_length`)\n - \u6b63\u5219\u8868\u8fbe\u5f0f\u9a8c\u8bc1 (`regex`)\n - \u679a\u4e3e\u9a8c\u8bc1 (`choices`)\n - \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u652f\u6301\n \n- **NumberField**: \u6570\u5b57\u5b57\u6bb5\uff08\u652f\u6301int\u3001float\u3001long\uff09\n - \u8303\u56f4\u9a8c\u8bc1 (`minvalue`, `maxvalue`)\n - \u679a\u4e3e\u9a8c\u8bc1 (`choices`)\n - \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u652f\u6301\n \n- **ListField**: \u6570\u7ec4\u5b57\u6bb5\n - \u957f\u5ea6\u9a8c\u8bc1 (`min_length`, `max_length`)\n - \u652f\u6301\u5d4c\u5957\u7c7b\u578b\u9a8c\u8bc1 (`item_type`)\n - \u652f\u6301\u5b57\u7b26\u4e32\u3001\u6570\u5b57\u3001dataclass\u6a21\u578b\u5d4c\u5957\n - \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u652f\u6301\n\n### \ud83c\udf0d \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n\n- **\u591a\u8bed\u8a00\u652f\u6301**: \u652f\u6301\u4e2d\u6587\u3001\u82f1\u6587\u7b49\u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\n- **\u6a21\u677f\u683c\u5f0f\u5316**: \u652f\u6301 `{\u53c2\u6570\u540d}` \u683c\u5f0f\u7684\u53c2\u6570\u66ff\u6362\n- **\u5b8c\u6574\u8986\u76d6**: \u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\u7684\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n- **\u5411\u540e\u517c\u5bb9**: \u4e0d\u5f71\u54cd\u73b0\u6709\u4ee3\u7801\uff0c\u53ef\u9009\u4f7f\u7528\n\n```python\n# \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u793a\u4f8b\n@dataclass\nclass User(object):\n name = StringField(\n min_length=3,\n max_length=20,\n error_messages={\n 'required': '\u7528\u6237\u540d\u662f\u5fc5\u586b\u9879',\n 'min_length': '\u7528\u6237\u540d\u81f3\u5c11\u9700\u8981 {min_length} \u4e2a\u5b57\u7b26',\n 'max_length': '\u7528\u6237\u540d\u4e0d\u80fd\u8d85\u8fc7 {max_length} \u4e2a\u5b57\u7b26'\n }\n )\n```\n\n### \ud83c\udfaf \u88c5\u9970\u5668\u8bed\u6cd5\n\n```python\n@dataclass\nclass User(object):\n name = StringField(min_length=1, max_length=100)\n age = NumberField(minvalue=0, maxvalue=150)\n```\n\n### \ud83d\udd0d \u81ea\u5b9a\u4e49\u9a8c\u8bc1\u88c5\u9970\u5668\n\n```python\n@dataclass\nclass Product(object):\n name = StringField()\n price = NumberField()\n \n @validate(\"name\")\n def validate_name_custom(self, name):\n if not name.isalnum():\n raise ValidationError(\"Name must be alphanumeric\")\n \n @validate(\"price\")\n def validate_price_custom(self, price):\n if price <= 0:\n raise ValidationError(\"Price must be positive\")\n```\n\n### \ud83d\udd27 \u81ea\u5b9a\u4e49get\u65b9\u6cd5\n\n```python\n@dataclass\nclass BlogPost(object):\n title = StringField()\n status = StringField(default='draft')\n \n def get_title(self):\n \"\"\"\u81ea\u5b9a\u4e49\u83b7\u53d6\u6807\u9898\u7684\u65b9\u6cd5\"\"\"\n title = self.__dict__.get('title', '')\n status = self.__dict__.get('status', 'draft')\n return \"[{0}] {1}\".format(status.upper(), title)\n```\n\n## \u5b8c\u6574\u793a\u4f8b\n\n### \ud83d\udcc1 \u793a\u4f8b\u6587\u4ef6\n\n\u9879\u76ee\u63d0\u4f9b\u4e86\u4e30\u5bcc\u7684\u793a\u4f8b\u6587\u4ef6\uff0c\u4f4d\u4e8e `examples/` \u76ee\u5f55\uff1a\n\n- **[\u57fa\u7840\u4f7f\u7528\u793a\u4f8b](examples/basic_usage.py)** - \u5b57\u6bb5\u7c7b\u578b\u3001dataclass \u57fa\u7840\u529f\u80fd\n- **[\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u793a\u4f8b](examples/custom_error_messages.py)** - \u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\u3001\u6a21\u677f\u683c\u5f0f\u5316\n- **[\u9ad8\u7ea7\u529f\u80fd\u793a\u4f8b](examples/advanced_features.py)** - \u81ea\u5b9a\u4e49\u9a8c\u8bc1\u3001\u5d4c\u5957 dataclass\u3001\u6761\u4ef6\u9a8c\u8bc1\n- **[\u5b9e\u9645\u5e94\u7528\u793a\u4f8b](examples/real_world_examples.py)** - \u7528\u6237\u7ba1\u7406\u3001\u7535\u5546\u4ea7\u54c1\u3001\u535a\u5ba2\u7cfb\u7edf\n\n### \ud83d\ude80 \u8fd0\u884c\u793a\u4f8b\n\n```bash\n# \u57fa\u7840\u4f7f\u7528\u793a\u4f8b\npython examples/basic_usage.py\n\n# \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u793a\u4f8b\npython examples/custom_error_messages.py\n\n# \u9ad8\u7ea7\u529f\u80fd\u793a\u4f8b\npython examples/advanced_features.py\n\n# \u5b9e\u9645\u5e94\u7528\u793a\u4f8b\npython examples/real_world_examples.py\n```\n\n### \ud83d\udca1 \u5feb\u901f\u793a\u4f8b\n\n#### \u7528\u6237\u7ba1\u7406\u7cfb\u7edf\n\n```python\nfrom schemas_dataclass import StringField, NumberField, ListField, dataclass, validate\n\n@dataclass\nclass User(object):\n username = StringField(\n min_length=3,\n max_length=20,\n regex=r'^[a-zA-Z][a-zA-Z0-9_]*$',\n error_messages={\n 'required': '\u7528\u6237\u540d\u662f\u5fc5\u586b\u9879',\n 'min_length': '\u7528\u6237\u540d\u81f3\u5c11\u9700\u8981 {min_length} \u4e2a\u5b57\u7b26',\n 'regex': '\u7528\u6237\u540d\u5fc5\u987b\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u53ea\u80fd\u5305\u542b\u5b57\u6bcd\u3001\u6570\u5b57\u548c\u4e0b\u5212\u7ebf'\n }\n )\n \n email = StringField(\n regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',\n error_messages={\n 'required': '\u90ae\u7bb1\u5730\u5740\u662f\u5fc5\u586b\u9879',\n 'regex': '\u8bf7\u8f93\u5165\u6709\u6548\u7684\u90ae\u7bb1\u5730\u5740'\n }\n )\n \n age = NumberField(\n minvalue=13,\n maxvalue=120,\n error_messages={\n 'minvalue': '\u5e74\u9f84\u4e0d\u80fd\u5c0f\u4e8e {minvalue} \u5c81',\n 'maxvalue': '\u5e74\u9f84\u4e0d\u80fd\u5927\u4e8e {maxvalue} \u5c81'\n }\n )\n \n tags = ListField(\n item_type=str,\n required=False,\n max_length=10,\n error_messages={\n 'max_length': '\u6807\u7b7e\u6570\u91cf\u4e0d\u80fd\u8d85\u8fc7 {max_length} \u4e2a'\n }\n )\n \n @validate(\"username\")\n def validate_username_not_reserved(self, username):\n \"\"\"\u68c0\u67e5\u7528\u6237\u540d\u662f\u5426\u4e3a\u4fdd\u7559\u8bcd\"\"\"\n reserved = ['admin', 'root', 'system']\n if username.lower() in reserved:\n raise ValidationError(f\"\u7528\u6237\u540d '{username}' \u662f\u7cfb\u7edf\u4fdd\u7559\u8bcd\")\n\n# \u4f7f\u7528\u793a\u4f8b\nuser = User(\n username=\"alice_dev\",\n email=\"alice@example.com\",\n age=28,\n tags=[\"developer\", \"python\"]\n)\n\nprint(\"\u7528\u6237: {}\".format(user.username))\nprint(\"\u90ae\u7bb1: {}\".format(user.email))\nprint(\"\u5e74\u9f84: {}\".format(user.age))\nprint(\"\u6807\u7b7e: {}\".format(user.tags))\n```\n\n## API \u53c2\u8003\n\n> **\u91cd\u8981\u53d8\u66f4\u8bf4\u660e**: \u4ece\u7248\u672c 2.0 \u5f00\u59cb\uff0c\u6240\u6709\u5b57\u6bb5\u9ed8\u8ba4\u4e3a\u53ef\u9009 (`required=False`)\u3002\u5982\u9700\u5fc5\u586b\u5b57\u6bb5\uff0c\u8bf7\u663e\u5f0f\u8bbe\u7f6e `required=True`\u3002\n\n### \u5b57\u6bb5\u7c7b\u578b\n\n#### StringField\n\n```python\nStringField(\n default=None, # \u9ed8\u8ba4\u503c\n alias=None, # \u5b57\u6bb5\u522b\u540d\n required=False, # \u662f\u5426\u5fc5\u586b (\u9ed8\u8ba4\u4e3a False)\n min_length=None, # \u6700\u5c0f\u957f\u5ea6\n max_length=None, # \u6700\u5927\u957f\u5ea6\n regex=None, # \u6b63\u5219\u8868\u8fbe\u5f0f\n choices=None, # \u679a\u4e3e\u9009\u9879\n error_messages=None # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n)\n```\n\n#### NumberField\n\n```python\nNumberField(\n default=None, # \u9ed8\u8ba4\u503c\n alias=None, # \u5b57\u6bb5\u522b\u540d\n required=False, # \u662f\u5426\u5fc5\u586b (\u9ed8\u8ba4\u4e3a False)\n minvalue=None, # \u6700\u5c0f\u503c\n maxvalue=None, # \u6700\u5927\u503c\n choices=None, # \u679a\u4e3e\u9009\u9879\n error_messages=None # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n)\n```\n\n#### ListField\n\n```python\nListField(\n default=None, # \u9ed8\u8ba4\u503c\n alias=None, # \u5b57\u6bb5\u522b\u540d\n required=False, # \u662f\u5426\u5fc5\u586b (\u9ed8\u8ba4\u4e3a False)\n min_length=None, # \u6700\u5c0f\u957f\u5ea6\n max_length=None, # \u6700\u5927\u957f\u5ea6\n item_type=None, # \u5217\u8868\u9879\u7c7b\u578b\n error_messages=None # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n)\n```\n\n### \u88c5\u9970\u5668\n\n#### @dataclass\n\n```python\n@dataclass\nclass MyClass(object):\n field1 = StringField()\n field2 = NumberField()\n```\n\n#### @validate\n\n```python\n@dataclass\nclass MyClass(object):\n field1 = StringField()\n\n @validate(\"field1\")\n def validate_field1(self, value):\n # \u81ea\u5b9a\u4e49\u9a8c\u8bc1\u903b\u8f91\n if not condition:\n raise ValidationError(\"Custom validation failed\")\n```\n\n### \u9519\u8bef\u6d88\u606f\u952e\n\n#### \u901a\u7528\u9519\u8bef\u6d88\u606f\u952e\n\n- `required`: \u5fc5\u586b\u5b57\u6bb5\u4e3a\u7a7a\n- `invalid_type`: \u7c7b\u578b\u4e0d\u5339\u914d\n\n#### StringField \u9519\u8bef\u6d88\u606f\u952e\n\n- `min_length`: \u957f\u5ea6\u5c0f\u4e8e\u6700\u5c0f\u503c\n- `max_length`: \u957f\u5ea6\u5927\u4e8e\u6700\u5927\u503c\n- `regex`: \u6b63\u5219\u8868\u8fbe\u5f0f\u5339\u914d\u5931\u8d25\n- `choices`: \u503c\u4e0d\u5728\u679a\u4e3e\u9009\u9879\u4e2d\n\n#### NumberField \u9519\u8bef\u6d88\u606f\u952e\n\n- `minvalue`: \u6570\u503c\u5c0f\u4e8e\u6700\u5c0f\u503c\n- `maxvalue`: \u6570\u503c\u5927\u4e8e\u6700\u5927\u503c\n- `choices`: \u503c\u4e0d\u5728\u679a\u4e3e\u9009\u9879\u4e2d\n\n#### ListField \u9519\u8bef\u6d88\u606f\u952e\n\n- `min_length`: \u5217\u8868\u957f\u5ea6\u5c0f\u4e8e\u6700\u5c0f\u503c\n- `max_length`: \u5217\u8868\u957f\u5ea6\u5927\u4e8e\u6700\u5927\u503c\n- `invalid_list_item`: \u5217\u8868\u9879\u7c7b\u578b\u4e0d\u5339\u914d\n\n## \u6d4b\u8bd5\n\n### \u8fd0\u884c\u6d4b\u8bd5\n\n```bash\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\npytest\n\n# \u8fd0\u884c\u7279\u5b9a\u6d4b\u8bd5\u6587\u4ef6\npytest tests/test_fields.py\n\n# \u8fd0\u884c\u5e26\u8986\u76d6\u7387\u7684\u6d4b\u8bd5\npytest --cov=schemas_dataclass\n\n# \u8fd0\u884c\u7279\u5b9a\u6807\u8bb0\u7684\u6d4b\u8bd5\npytest -m \"unit\"\npytest -m \"integration\"\npytest -m \"error_messages\"\n```\n\n### \u6d4b\u8bd5\u7ed3\u6784\n\n```\ntests/\n\u251c\u2500\u2500 conftest.py # pytest \u914d\u7f6e\u548c fixtures\n\u251c\u2500\u2500 test_fields.py # \u5b57\u6bb5\u7c7b\u578b\u6d4b\u8bd5\n\u251c\u2500\u2500 test_custom_error_messages.py # \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u6d4b\u8bd5\n\u251c\u2500\u2500 test_dataclass.py # dataclass \u529f\u80fd\u6d4b\u8bd5\n\u2514\u2500\u2500 test_integration.py # \u96c6\u6210\u6d4b\u8bd5\n```\n\n### \u6d4b\u8bd5\u8986\u76d6\n\n- **25+ \u4e2a\u6d4b\u8bd5\u7528\u4f8b**\uff0c\u8986\u76d6\u6240\u6709\u529f\u80fd\u70b9\n- **100% \u6d4b\u8bd5\u901a\u8fc7\u7387**\n- **\u5411\u540e\u517c\u5bb9\u6027\u9a8c\u8bc1**\n- **\u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\u6d4b\u8bd5**\n- **\u590d\u6742\u573a\u666f\u8fb9\u754c\u6d4b\u8bd5**\n\n## \u9a8c\u8bc1\u7279\u6027\n\n### \u5b57\u7b26\u4e32\u9a8c\u8bc1\n\n- \u957f\u5ea6\u9a8c\u8bc1\uff1a`min_length`, `max_length`\n- \u6b63\u5219\u8868\u8fbe\u5f0f\u9a8c\u8bc1\uff1a`regex`\n- \u679a\u4e3e\u9a8c\u8bc1\uff1a`choices`\n- \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\uff1a\u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\n\n### \u6570\u5b57\u9a8c\u8bc1\n\n- \u8303\u56f4\u9a8c\u8bc1\uff1a`minvalue`, `maxvalue`\n- \u679a\u4e3e\u9a8c\u8bc1\uff1a`choices`\n- \u7c7b\u578b\u9a8c\u8bc1\uff1a\u81ea\u52a8\u652f\u6301int\u3001float\u3001long\uff08Python 2\uff09\n- \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\uff1a\u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\n\n### \u6570\u7ec4\u9a8c\u8bc1\n\n- \u957f\u5ea6\u9a8c\u8bc1\uff1a`min_length`, `max_length`\n- \u9879\u7c7b\u578b\u9a8c\u8bc1\uff1a`item_type`\n- \u652f\u6301\u5d4c\u5957\uff1a\u5b57\u7b26\u4e32\u3001\u6570\u5b57\u3001dataclass\u6a21\u578b\n- \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\uff1a\u652f\u6301\u5217\u8868\u9879\u7c7b\u578b\u9519\u8bef\n\n### DataClass\u5b57\u6bb5\u652f\u6301\n\n- \u652f\u6301dataclass\u4f5c\u4e3a\u5b57\u6bb5\u7c7b\u578b\n- \u81ea\u52a8\u5b9e\u4f8b\u5316\u548c\u9a8c\u8bc1\n- \u91cd\u65b0\u8d4b\u503c\u65f6\u91cd\u65b0\u521b\u5efa\u5bf9\u8c61\n- \u652f\u6301\u5d4c\u5957\u7684to_dict()\u8f6c\u6362\n\n### \u81ea\u5b9a\u4e49\u9a8c\u8bc1\n\n- \u4f7f\u7528`@validate(\"field_name\")`\u88c5\u9970\u5668\n- \u5728\u57fa\u7840\u9a8c\u8bc1\u4e4b\u540e\u6267\u884c\n- \u652f\u6301\u591a\u4e2a\u81ea\u5b9a\u4e49\u9a8c\u8bc1\u51fd\u6570\n\n### \u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u7279\u6027\n\n- **\u591a\u8bed\u8a00\u652f\u6301**\uff1a\u5b8c\u5168\u652f\u6301\u4e2d\u6587\u3001\u82f1\u6587\u7b49\u591a\u8bed\u8a00\u9519\u8bef\u6d88\u606f\n- **\u6a21\u677f\u683c\u5f0f\u5316**\uff1a\u652f\u6301 `{\u53c2\u6570\u540d}` \u683c\u5f0f\u7684\u53c2\u6570\u66ff\u6362\uff0c\u5982 `{min_length}`, `{maxvalue}` \u7b49\n- **\u5b8c\u6574\u8986\u76d6**\uff1a\u652f\u6301\u6240\u6709\u9a8c\u8bc1\u7c7b\u578b\u7684\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\n- **\u5411\u540e\u517c\u5bb9**\uff1a\u4e0d\u5f71\u54cd\u73b0\u6709\u4ee3\u7801\uff0c\u53ef\u9009\u4f7f\u7528\n- **\u5065\u58ee\u6027**\uff1a\u683c\u5f0f\u5316\u5931\u8d25\u65f6\u4f18\u96c5\u964d\u7ea7\uff0c\u8fd4\u56de\u539f\u59cb\u6a21\u677f\n- **\u96f6\u6027\u80fd\u5f71\u54cd**\uff1a\u4e0d\u4f7f\u7528\u81ea\u5b9a\u4e49\u6d88\u606f\u65f6\u6027\u80fd\u4e0e\u539f\u7248\u672c\u5b8c\u5168\u76f8\u540c\n\n#### \u652f\u6301\u7684\u9519\u8bef\u6d88\u606f\u7c7b\u578b\n\n- **\u901a\u7528**: `required`, `invalid_type`\n- **StringField**: `min_length`, `max_length`, `regex`, `choices`\n- **NumberField**: `minvalue`, `maxvalue`, `choices`\n- **ListField**: `min_length`, `max_length`, `invalid_list_item`\n\n## \u517c\u5bb9\u6027\n\n- **Python 2.7+**: \u5b8c\u5168\u652f\u6301\n- **Python 3.4+**: \u5b8c\u5168\u652f\u6301\n- **PyPy**: \u652f\u6301\n- **Jython**: \u7406\u8bba\u652f\u6301\uff08\u672a\u6d4b\u8bd5\uff09\n\n## \u6027\u80fd\n\n- **\u96f6\u4f9d\u8d56**: \u4ec5\u4f7f\u7528 Python \u6807\u51c6\u5e93\n- **\u8f7b\u91cf\u7ea7**: \u6838\u5fc3\u4ee3\u7801\u4e0d\u5230 1000 \u884c\n- **\u9ad8\u6027\u80fd**: \u9a8c\u8bc1\u901f\u5ea6\u5feb\uff0c\u5185\u5b58\u5360\u7528\u4f4e\n- **\u53ef\u6269\u5c55**: \u6613\u4e8e\u6dfb\u52a0\u65b0\u7684\u5b57\u6bb5\u7c7b\u578b\u548c\u9a8c\u8bc1\u89c4\u5219\n\n## \u8d21\u732e\u6307\u5357\n\n\u6b22\u8fce\u8d21\u732e\u4ee3\u7801\uff01\u8bf7\u9075\u5faa\u4ee5\u4e0b\u6b65\u9aa4\uff1a\n\n1. Fork \u9879\u76ee\n2. \u521b\u5efa\u529f\u80fd\u5206\u652f (`git checkout -b feature/amazing-feature`)\n3. \u6dfb\u52a0\u6d4b\u8bd5\u7528\u4f8b\n4. \u786e\u4fdd\u6240\u6709\u6d4b\u8bd5\u901a\u8fc7 (`pytest`)\n5. \u66f4\u65b0\u76f8\u5173\u6587\u6863\n6. \u63d0\u4ea4\u66f4\u6539 (`git commit -m 'Add amazing feature'`)\n7. \u63a8\u9001\u5230\u5206\u652f (`git push origin feature/amazing-feature`)\n8. \u521b\u5efa Pull Request\n\n### \u5f00\u53d1\u73af\u5883\u8bbe\u7f6e\n\n```bash\ngit clone https://github.com/schemas/dataclass.git\ncd dataclass\npip install -e .\npip install -r requirements-dev.txt\n```\n\n### \u4ee3\u7801\u89c4\u8303\n\n- \u9075\u5faa PEP 8 \u4ee3\u7801\u98ce\u683c\n- \u6dfb\u52a0\u9002\u5f53\u7684\u6587\u6863\u5b57\u7b26\u4e32\n- \u4e3a\u65b0\u529f\u80fd\u6dfb\u52a0\u6d4b\u8bd5\u7528\u4f8b\n- \u4fdd\u6301 Python 2/3 \u517c\u5bb9\u6027\n\n## \u8bb8\u53ef\u8bc1\n\n\u672c\u9879\u76ee\u91c7\u7528 GNU General Public License v3.0 \u8bb8\u53ef\u8bc1\u3002\u8be6\u89c1 [LICENSE](LICENSE) \u6587\u4ef6\u3002\n\n## \u66f4\u65b0\u65e5\u5fd7\n\n\u67e5\u770b [CHANGELOG.md](CHANGELOG.md) \u4e86\u89e3\u8be6\u7ec6\u7684\u7248\u672c\u66f4\u65b0\u8bb0\u5f55\u3002\n\n---\n\n**\u6ce8\u610f**: \u672c\u5e93\u5b8c\u5168\u517c\u5bb9 Python 2.7 \u548c Python 3.x\uff0c\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f\u529f\u80fd\u4e3a\u53ef\u9009\u7279\u6027\uff0c\u4e0d\u5f71\u54cd\u73b0\u6709\u4ee3\u7801\u7684\u4f7f\u7528\u3002\n",
"bugtrack_url": null,
"license": null,
"summary": "Python 2/3 \u517c\u5bb9\u7684 DataClass \u5e93\uff0c\u652f\u6301\u5b8c\u6574\u7684\u6570\u636e\u6821\u9a8c\u529f\u80fd\u548c\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f",
"version": "0.0.1",
"project_urls": {
"Bug Reports": "https://github.com/b40yd/schemas-python/issues",
"Changelog": "https://github.com/b40yd/schemas-python/blob/main/CHANGELOG.md",
"Documentation": "https://github.com/b40yd/schemas-python/blob/main/README.md",
"Homepage": "https://github.com/b40yd/schemas-python",
"Repository": "https://github.com/b40yd/schemas-python.git"
},
"split_keywords": [
"dataclass",
" fields",
" python2",
" python3",
" schema",
" validation"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "9f13485c06d4b1a9a7951077a3727c3b91fa338e21f00f6d8343dfb23710ba26",
"md5": "a5ebb4b9e9e7e2b11b37a200f2f09de2",
"sha256": "8c193bde414dc9fd7355bb11cc1054419fab97f2ebc2920cf8e567aaeff5b30a"
},
"downloads": -1,
"filename": "schemas_dataclass-0.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a5ebb4b9e9e7e2b11b37a200f2f09de2",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 26734,
"upload_time": "2025-08-23T06:20:18",
"upload_time_iso_8601": "2025-08-23T06:20:18.542174Z",
"url": "https://files.pythonhosted.org/packages/9f/13/485c06d4b1a9a7951077a3727c3b91fa338e21f00f6d8343dfb23710ba26/schemas_dataclass-0.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "4687780af591c6fb0ce9420705ae2e3860833efb4feeecb97533113a03508eb5",
"md5": "2bc047daeb5ad11082b85ffba10bdba4",
"sha256": "61bb53d899f721aad8e62bb03c4b9c02003f879cc1d45b4afaf8b53f97c8f544"
},
"downloads": -1,
"filename": "schemas_dataclass-0.0.1.tar.gz",
"has_sig": false,
"md5_digest": "2bc047daeb5ad11082b85ffba10bdba4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 54710,
"upload_time": "2025-08-23T06:20:20",
"upload_time_iso_8601": "2025-08-23T06:20:20.584561Z",
"url": "https://files.pythonhosted.org/packages/46/87/780af591c6fb0ce9420705ae2e3860833efb4feeecb97533113a03508eb5/schemas_dataclass-0.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-23 06:20:20",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "b40yd",
"github_project": "schemas-python",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"tox": true,
"lcname": "schemas-dataclass"
}