验证在 Python 中有许多属性的 class 的属性的最佳方法是什么?
What is the best way to validate attributes of a class that have many attributes in Python?
我在 API 中工作,我必须验证用户在 post/put 端点中写入的数据。但是我的模型 class 有 10 多个属性,所以我必须用 10 个 ifs 来验证它们,所以这让我的代码看起来很糟糕。
示例:
class MyClass():
atr1: str
atr2: str
atr3: str
atr4: str
atr5: str
atr6: str
atr7: str
atr8: str
atr9: str
atr10: str
然后我有我的放置端点:
@api.put("/update/{id}")
def update(id: int, exemple: MyClass):
if exemple not in exemples:
return {"Error": "exemple not found!"}
if exemple.atr1 != None:
exemples[exemple].atr1 = exemple.atr1
if exemple.atr2 != None:
exemples[exemple].atr2 = exemple.atr2
if exemple.atr3 != None:
exemples[exemple].atr3 = exemple.atr3
if exemple.atr4 != None:
exemples[exemple].atr4 = exemple.atr4
if exemple.atr5 != None:
exemples[exemple].atr5 = exemple.atr5
if exemple.atr6 != None:
exemples[exemple].atr6 = exemple.atr6
if exemple.atr7 != None:
exemples[exemple].atr7 = exemple.atr7
if exemple.atr8 != None:
exemples[exemple].atr8 = exemple.atr8
if exemple.atr9 != None:
exemples[exemple].atr9 = exemple.atr9
if exemple.atr10 != None:
exemples[exemple].atr10 = exemple.atr10
return exemples[exemple]
我知道我可以在另一个地方做这个验证,但我仍然需要做 10 个 ifs 来验证每个字段。我正在寻找一种方法来获取每个属性的名称,然后在 for 循环内的 if 语句中使用 for 和 validade 循环。我正在寻找方法来做到这一点。我找到了一种使用 __dict__
, vars(MyClass) 的方法,但我无法让它工作。任何人都可以帮助我以简单的方式做到这一点吗?
我可以建议两种方法来执行此操作并避免这些 if else 块。
首先:
您可以使用属性。对于 class 中的每个属性,添加一个 属性(getter 和 setter)。这样,每次从任何地方将新值分配给 class 的每个属性时,您都会进行验证。
(您应该牢记的一个重要注意事项是 I/O 任务中的属性很慢)。
class MyClass:
_atr1: float
@property
def atr1(self) -> float:
"""
This is getter method and just return the
_atr1 attribute.
"""
return self._atr1
@atr1.setter
def atr1(self, value) -> None:
"""
This is setter method for _atr1 and has a
little validation. If validation will not pass
It will raise an ValueError.
"""
if value > 0:
self._atr1 = value
return
raise ValueError("The value is not enough!")
秒:
您可以使用描述符。通过这种方式,您可以在 class 之外添加许多描述符,并将这些描述符分配给您的属性。描述符相对于属性的一个重要优势是您可以在许多 classes 中使用它们并且它们具有更快的速度。
class NumberDescriptor(object):
"""
This class is a descriptor which validate the value
to be greater than 0
"""
def __init__(self, number=None):
self.number = number
def __get__(self, obj, objtype):
return self.number
def __set__(self, obj, number):
if number > 0:
self.number = number
else:
raise ValueError("The value is not enough!")
class MyClass:
atr1 = NumberDescriptor()
atr2 = NumberDescriptor()
atr3 = NumberDescriptor()
c = MyClass()
c.atr1 = 0
ValueError: The value is not enough!
您可以使用 Pydantic 验证器,它们可以很好地与 FastAPI 配合使用,并且更易于阅读或维护:
from pydantic import BaseModel, validator
class MyClass(BaseModel):
attr1: str
attr2: str
[..]
@validator('attr1')
def attr1_validations(cls, v, values):
# example (v is attr1)
if len(v) > 255:
raise ValueError(f"Length cannot be greater than 255!")
elif v < 1:
raise ValueError("Length cannot be lesser than 1!!")
return v
但是,如果您只想检查输入中是否给出了所有字段并检查它们的长度,则可以使用 constr
from pydantic import BaseModel, validator, constr
class MyClass(BaseModel):
attr1: constr(min_length=1, max_length=64)
attr2: constr(min_length=1, max_length=128)
[..]
请注意,Pydantic 模型中列出的所有字段都必须在向 FastAPI 服务器发出的请求中,否则将给出自动响应,提示输入无效。
如果你希望有可选字段,你可以这样做:
from pydantic import BaseModel, constr
from typing import Optional
class MyClass(BaseModel):
attr1: constr(min_length=1, max_length=64)
attr2: Optional[constr(min_length=1, max_length=64)] # this field can be skipped in the request body
请仔细阅读 FastAPI 和 Pydantic 的文档,它们提供了所有需要的信息:
我在 API 中工作,我必须验证用户在 post/put 端点中写入的数据。但是我的模型 class 有 10 多个属性,所以我必须用 10 个 ifs 来验证它们,所以这让我的代码看起来很糟糕。
示例:
class MyClass():
atr1: str
atr2: str
atr3: str
atr4: str
atr5: str
atr6: str
atr7: str
atr8: str
atr9: str
atr10: str
然后我有我的放置端点:
@api.put("/update/{id}")
def update(id: int, exemple: MyClass):
if exemple not in exemples:
return {"Error": "exemple not found!"}
if exemple.atr1 != None:
exemples[exemple].atr1 = exemple.atr1
if exemple.atr2 != None:
exemples[exemple].atr2 = exemple.atr2
if exemple.atr3 != None:
exemples[exemple].atr3 = exemple.atr3
if exemple.atr4 != None:
exemples[exemple].atr4 = exemple.atr4
if exemple.atr5 != None:
exemples[exemple].atr5 = exemple.atr5
if exemple.atr6 != None:
exemples[exemple].atr6 = exemple.atr6
if exemple.atr7 != None:
exemples[exemple].atr7 = exemple.atr7
if exemple.atr8 != None:
exemples[exemple].atr8 = exemple.atr8
if exemple.atr9 != None:
exemples[exemple].atr9 = exemple.atr9
if exemple.atr10 != None:
exemples[exemple].atr10 = exemple.atr10
return exemples[exemple]
我知道我可以在另一个地方做这个验证,但我仍然需要做 10 个 ifs 来验证每个字段。我正在寻找一种方法来获取每个属性的名称,然后在 for 循环内的 if 语句中使用 for 和 validade 循环。我正在寻找方法来做到这一点。我找到了一种使用 __dict__
, vars(MyClass) 的方法,但我无法让它工作。任何人都可以帮助我以简单的方式做到这一点吗?
我可以建议两种方法来执行此操作并避免这些 if else 块。
首先: 您可以使用属性。对于 class 中的每个属性,添加一个 属性(getter 和 setter)。这样,每次从任何地方将新值分配给 class 的每个属性时,您都会进行验证。 (您应该牢记的一个重要注意事项是 I/O 任务中的属性很慢)。
class MyClass:
_atr1: float
@property
def atr1(self) -> float:
"""
This is getter method and just return the
_atr1 attribute.
"""
return self._atr1
@atr1.setter
def atr1(self, value) -> None:
"""
This is setter method for _atr1 and has a
little validation. If validation will not pass
It will raise an ValueError.
"""
if value > 0:
self._atr1 = value
return
raise ValueError("The value is not enough!")
秒: 您可以使用描述符。通过这种方式,您可以在 class 之外添加许多描述符,并将这些描述符分配给您的属性。描述符相对于属性的一个重要优势是您可以在许多 classes 中使用它们并且它们具有更快的速度。
class NumberDescriptor(object):
"""
This class is a descriptor which validate the value
to be greater than 0
"""
def __init__(self, number=None):
self.number = number
def __get__(self, obj, objtype):
return self.number
def __set__(self, obj, number):
if number > 0:
self.number = number
else:
raise ValueError("The value is not enough!")
class MyClass:
atr1 = NumberDescriptor()
atr2 = NumberDescriptor()
atr3 = NumberDescriptor()
c = MyClass()
c.atr1 = 0
ValueError: The value is not enough!
您可以使用 Pydantic 验证器,它们可以很好地与 FastAPI 配合使用,并且更易于阅读或维护:
from pydantic import BaseModel, validator
class MyClass(BaseModel):
attr1: str
attr2: str
[..]
@validator('attr1')
def attr1_validations(cls, v, values):
# example (v is attr1)
if len(v) > 255:
raise ValueError(f"Length cannot be greater than 255!")
elif v < 1:
raise ValueError("Length cannot be lesser than 1!!")
return v
但是,如果您只想检查输入中是否给出了所有字段并检查它们的长度,则可以使用 constr
from pydantic import BaseModel, validator, constr
class MyClass(BaseModel):
attr1: constr(min_length=1, max_length=64)
attr2: constr(min_length=1, max_length=128)
[..]
请注意,Pydantic 模型中列出的所有字段都必须在向 FastAPI 服务器发出的请求中,否则将给出自动响应,提示输入无效。 如果你希望有可选字段,你可以这样做:
from pydantic import BaseModel, constr
from typing import Optional
class MyClass(BaseModel):
attr1: constr(min_length=1, max_length=64)
attr2: Optional[constr(min_length=1, max_length=64)] # this field can be skipped in the request body
请仔细阅读 FastAPI 和 Pydantic 的文档,它们提供了所有需要的信息: