简体   繁体   English

如何在python中验证数据的最佳方法是什么?

[英]How is the best approach to validate data in python?

I am a newbie in Python, and I´m trying to find the best way to validate data. 我是Python的新手,我正在尝试寻找验证数据的最佳方法。

I have an object of type "well" that has as attributes other objects. 我有一个“ well”类型的对象,该对象具有其他对象的属性。 Data can come from a XML file or via code. 数据可以来自XML文件或通过代码。 An example can be seen bellow. 可以看到一个例子。


class Well:
    def __init__ (self, name, group):
        self.__name   = name     # Required
        self.__group  = group    # Required
        self.__operate_list = [] # Optional
        self.__monitor_list = [] # Optional
        self.__geometry = None   # Optional
        self.__perf = None       # Optional
        ...

class Operate:

    # *OPERATE (*MAX|*MIN) type value (action)
    # *OPERATE (*PENALTY) type (mode) value (action)
    # *OPERATE (*WCUTBACK) type mode v1 (v2 (v3)) (action)

    def __init__ (self, att:str, type_:str, value: [], mode=None, action=None):
        self.__att = att
        self.__type_ = type_
        self.__mode = mode
        self.__value_list = value
        self.__action = action        

To validate an "operate" for example, i need to check a lot of restrictions and valid values for each attribute. 例如,要验证“操作”,我需要检查每个属性的许多限制和有效值。 For instance, I have a list of valid "type_" strings and I should assert that type_ is in this list. 例如,我有一个有效的“ type_”字符串列表,我应该断言type_在此列表中。

1) The best approach to do this is in the constructor? 1)最好的方法是在构造函数中? Should I create a method to do this validation? 我应该创建一种方法来执行此验证吗? Or should I create a new class only to validade data? 还是应该仅创建一个新类来验证数据?

2) Where should I create these lists of valid values? 2)我应该在哪里创建这些有效值列表? In the constructor? 在构造函数中? As global variables? 作为全局变量?

You can use getter and setter methods via the use of the property function: 您可以通过使用property函数来使用getter和setter方法:

class Operate:
    def __init__(self, type):
        self.type = type

    @property
    def type(self):
        return self._type

    @type.setter
    def type(self, value):
        assert value in ('abc', 'xyz')
        self._type = value

so that: 以便:

o = Operate(type='123')

would result in: 会导致:

Traceback (most recent call last):
  File "test.py", line 18, in <module>
    o = Operate(type='123')
  File "test.py", line 8, in __init__
    self.type = type
  File "test.py", line 15, in type
    assert value in ('abc', 'xyz')
AssertionError
assert isinstance(obj) 

Is how you test the type of an object. 是如何测试对象的类型。

if item in container: ...

Is how you would test if an object is in a container. 是如何测试对象是否在容器中的方法。

Whether you do this in the init method or in another method is up to you, it depends which looks cleaner to you, or if you would need to reuse the functionality. init方法中还是在其他方法中执行此操作,取决于您选择哪种方法更清洁,或者是否需要重用该功能。

The list of valid values could be passed into the init method or hardcoded into the init method. 有效值列表可以传递到init方法中,也可以硬编码到init方法中。 It can also be a global property of the class. 它也可以是该类的全局属性。

You could do it with descriptors. 您可以使用描述符来实现。 The only advantage I can contrive is that it puts the validation in another class - making the class that uses it less verbose. 我能想到的唯一优点是,它将验证放入另一个类中-使使用它的类不再那么冗长。 Unfortunately you would have to make one for each attribute with unique validations, Unless you want to include options for membership tests and/or instance-of tests which shouldn't make it too complicated. 不幸的是,除非必须为成员资格测试和/或实例测试提供选项,否则就必须为每个属性创建一个唯一的验证,而这并不会使其变得过于复杂。

from weakref import WeakKeyDictionary

class RestrictedAttribute:
    """A descriptor that restricts values"""
    def __init__(self, restrictions):
        self.restrictions = restrictions
        self.data = WeakKeyDictionary()

    def __get__(self, instance, owner):
        return self.data.get(instance, None)

    def __set__(self, instance, value):
        if value not in self.restrictions:
            raise ValueError(f'{value} is not allowed')
        self.data[instance] = value

When used the descriptor instance must be assigned as a class attribute 使用描述符实例时,必须将其分配为类属性

class Operate:

    __type_ = RestrictedAttribute(('red','blue'))

    def __init__ (self, att:str, type_:str, value: [], mode=None, action=None):
        self.__att = att
        self.__type_ = type_
        self.__mode = mode
        self.__value_list = value
        self.__action = action        

In use: 正在使用:

In [15]: o = Operate('f',type_='blue',value=[1,2])

In [16]: o._Operate__type_
Out[16]: 'blue'

In [17]: o._Operate__type_ = 'green'
Traceback (most recent call last):

  File "<ipython-input-17-b412cfaa0cb0>", line 1, in <module>
    o._Operate__type_ = 'green'

  File "P:/pyProjects3/tmp1.py", line 28, in __set__
    raise ValueError(msg)

ValueError: green is not allowed

You can do this with pyfields , which can be seen as an industrialized version of wwii's answer . 您可以使用pyfields进行此pyfields ,可以将其视为wwii的答案的工业化版本。

You define a field using field() . 您可以使用field()定义一个字段。 Fields can be mandatory or optional, and in case the default value is mutable you can specify a default factory (I saw that you use empty lists as default values in your example). 字段可以是必填字段,也可以是可选字段,如果默认值是可变的,则可以指定默认工厂(在示例中,我看到您使用空列表作为默认值)。 Helper function copy_value helps you cover the most common case: 辅助函数copy_value可帮助您解决最常见的情况:

from pyfields import field, copy_value, init_fields
from valid8.validation_lib import is_in

class Well(object):
    name = field()                                        # Required
    group = field()                                       # Required
    operate_list = field(default_factory=copy_value([]))  # Optional
    monitor_list = field(default_factory=copy_value([]))  # Optional
    geometry = field(default=None)                        # Optional
    perf = field(default=None)                            # Optional

Then you can optionally add validation on top of your fields. 然后,您可以选择在字段上方添加验证。 You can both validate type (with check_type=True ) and value (with validators ). 可以既验证型(带check_type=True )和值(与validators )。 Validators can rely on existing callables such as is_in as shown below, but generally can leverage any validation callable. 验证器可以依赖现有的可调用对象,例如is_in ,如下所示,但通常可以利用任何验证可调用对象。 Finally the constructor can be generated for you, as shown below: 最后,可以为您生成构造函数,如下所示:

valid_types = ('type_A', 'type_B')

class Operate(object):
    att = field()                                  # Required
    type_: str = field(check_type=True, validators=is_in(valid_types))   # Required
    value = field(default_factory=copy_value([]))  # Optional
    mode = field(default=None)                     # Optional
    action = field(default=None)                   # Optional

    @init_fields
    def __init__(self):
        pass

o = Operate(att="foo", type_='type_A')  # ok
o.type_ = 1  # <-- raises TypeError: Invalid value type provided

bad_o = Operate(att="foo", type_='type_WRONG')  # <-- raises ValidationError: NotInAllowedValues: x in ('type_A', 'type_B') does not hold for x=type_WRONG

See pyfields documentation for details. 有关详细信息,请参见pyfields文档 I'm the author by the way ;) 我是作者;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM