gpt4 book ai didi

python - 改变数据类时验证输入

转载 作者:太空狗 更新时间:2023-10-30 02:24:17 25 4
gpt4 key购买 nike

在 Python 3.7 中有这些新的“数据类”容器,它们基本上类似于可变命名元组。假设我制作了一个代表一个人的数据类。我可以通过 __post_init__() 函数添加输入验证,如下所示:

@dataclass
class Person:
name: str
age: float

def __post_init__(self):
if type(self.name) is not str:
raise TypeError("Field 'name' must be of type 'str'.")
self.age = float(self.age)
if self.age < 0:
raise ValueError("Field 'age' cannot be negative.")

这将使良好的输入通过:

someone = Person(name="John Doe", age=30)
print(someone)

Person(name='John Doe', age=30.0)

虽然所有这些错误的输入都会引发错误:

someone = Person(name=["John Doe"], age=30)
someone = Person(name="John Doe", age="thirty")
someone = Person(name="John Doe", age=-30)

但是,由于数据类是可变的,我可以这样做:

someone = Person(name="John Doe", age=30)
someone.age = -30
print(someone)

Person(name='John Doe', age=-30)

从而绕过输入验证。

那么,确保数据类的字段在 初始化后不会突变为不良内容的最佳方法是什么?

最佳答案

数据类是一种提供默认初始化以接受属性作为参数的机制,以及一个很好的表示,以及一些诸如 __post_init__ 钩子(Hook)之类的细节。

幸运的是,它们不会与 Python 中的任何其他属性访问机制混淆 - 您仍然可以将无数据类属性创建为 property 描述符,或者如果需要,也可以创建自定义描述符类。这样,任何属性访问都将自动通过您的 getter 和 setter 函数。

使用默认的 property 内置的唯一缺点是你必须以“旧方式”使用它,而不是使用装饰器语法 - 这允许你为你的创建注释属性。

因此,“描述符”是在 Python 中分配给类属性的特殊对象,任何对该属性的访问都会调用描述符 __get____set____del__ 方法。内置的 property 可以方便地构建一个描述符,传递 1 到 3 个函数,这些函数将被这些方法调用。

因此,在没有自定义描述符的情况下,您可以:

@dataclass
class MyClass:
def setname(self, value):
if not isinstance(value, str):
raise TypeError(...)
self.__dict__["name"] = value
def getname(self):
return self.__dict__.get("name")
name: str = property(getname, setname)
# optionally, you can delete the getter and setter from the class body:
del setname, getname

通过使用这种方法,您必须将每个属性的访问权限编写为两个方法/函数,但不再需要编写您的__post_init__:每个属性都会自行验证。

另请注意,此示例采用了将属性通常存储在实例的 __dict__ 中的小常用方法。在网络上的示例中,实践是使用正常的属性访问,但在名称前加上 _。这将使这些属性污染您最终实例上的 dir,并且私有(private)属性将不 protected 。

另一种方法是编写您自己的描述符类,并让它检查您要保护的属性的实例和其他属性。这可以根据您的需要进行复杂化,最终以您自己的框架结束。因此,对于将检查属性类型并接受验证器列表的描述符类,您将需要:

def positive_validator(name, value):
if value <= 0:
raise ValueError(f"values for {name!r} have to be positive")

class MyAttr:
def __init__(self, type, validators=()):
self.type = type
self.validators = validators

def __set_name__(self, owner, name):
self.name = name

def __get__(self, instance, owner):
if not instance: return self
return instance.__dict__[self.name]

def __delete__(self, instance):
del instance.__dict__[self.name]

def __set__(self, instance, value):
if not isinstance(value, self.type):
raise TypeError(f"{self.name!r} values must be of type {self.type!r}")
for validator in self.validators:
validator(self.name, value)
instance.__dict__[self.name] = value

#And now

@dataclass
class Person:
name: str = MyAttr(str)
age: float = MyAttr((int, float), [positive_validator,])

就是这样——创建你自己的描述符类需要更多的 Python 知识,但上面给出的代码应该很好用,即使在生产中——欢迎你使用它。

请注意,您可以轻松地为每个属性添加许多其他检查和转换 -并且 __set_name__ 本身的代码可以更改为内省(introspection) owner 类中的 __annotations__ 以自动记录类型 - 这样类型MyAttr 类本身不需要参数。但正如我之前所说:您可以根据需要将其复杂化。

关于python - 改变数据类时验证输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54488765/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com