使用attr验证同一对象字段的两个字段

时间:2019-02-22 10:53:37

标签: python validation attr

我想验证对象初始化后的两个字段。我使用attrs定义元类PropertyMeta,然后在另一个类PropertyDescriptor中使用PropertyMeta。

class PropertyMeta(type):
        EXPECTED_VARS = ('PROPERTIES', 'PROPERTY_DEFAULTS',
                         'PROPERTY_VALIDATORS', 'PROPERTY_CONVERTERS')
        @classmethod
        def _get_attrib(mcs, property_name, attrs):
            kwargs = {}
            if property_name in attrs['PROPERTY_DEFAULTS']:
                kwargs.update(default=attrs['PROPERTY_DEFAULTS'][property_name])
            return attr.attrib(**kwargs)

    def __new__(mcs, name, bases, attrs):
        if 'PROPERTIES' not in attrs:
            attrs['PROPERTIES'] = []
        else:
            # For the defaults, converters, and validators ensure that extra attributes are not passed.
            # If they are defined, set the default value as an empty dict.
            for expected_attr in mcs.EXPECTED_VARS:
                if expected_attr == 'PROPERTIES':
                    continue

                if expected_attr in attrs:
                    for arg in attrs[expected_attr]:
                        if arg not in attrs['PROPERTIES']:
                            raise ValueError(
                                "Found a '%s' for an attribute '%s' in '%s' that doesn't belong to 'PROPERTIES';"
                                " ('PROPERTIES': %r, '%s': %r)" %
                                (arg, expected_attr, name,
                                 attrs['PROPERTIES'], expected_attr, attrs[expected_attr])
                            )
                else:
                    attrs[expected_attr] = {}

        # Create class from attr with all required attributes. This will set properties in same format as specified
        # by attr.
        new_cls = attr.make_class(name,
                                  {property_name: mcs._get_attrib(property_name, attrs) for property_name in
                                   attrs['PROPERTIES']})
        # Merge the attr class to present class.
        return super(PropertyMeta, mcs).__new__(mcs, name, (new_cls,) + bases, attrs)


@six.add_metaclass(PropertyMeta)
class PropertyDescriptor(object):
    def __init__(self):
        print("Not getting  called.")

    def __setattr__(self, property_name, property_value):
        # Convert properties to appropriate types if specified.
        if property_name in self.PROPERTY_CONVERTERS:
            property_value = self.PROPERTY_CONVERTERS[property_name](
                property_value)

        # Validate property values.
        if property_name in self.PROPERTY_VALIDATORS:
            try:
                validator = self.PROPERTY_VALIDATORS[property_name]
                validator(self, property_name, property_value)
            except Exception as err:
                raise err
        super(PropertyDescriptor, self).__setattr__(property_name, property_value)

我正在尝试使用PropertyDescriptor作为基类实例化一个对象。每当我的对象被实例化时,PropertyDescriptor的 init 都不会被调用。

从PropertyDescriptor继承的示例。

class Example(PropertyDescriptor):
        def __init__(self, **kwargs):
            super(A, self).__init__(**kwargs)
            print(self.__dict__)

a = Example(start_time=1, end_time=2)
Example类的

init 被调用。因此,我可以验证开始时间始终小于结束时间的字段

两个问题:

  1. 我想确保start_time总是小于end_time。有没有更好的方法来实现这一目标?
  2. 为什么没有在PropertyDescriptor上调用 init

0 个答案:

没有答案