正确的方法来validation类的实例的属性

有这样一个简单的Python类:

class Spam(object): __init__(self, description, value): self.description = description self.value = value 

我想检查以下约束:

  • “描述不能为空”
  • “值必须大于零”

我是不是该:
1.创build垃圾邮件对象之前validation数据?
2.检查__init__方法的数据?
3.在Spam类上创build一个is_valid方法,并用spam.isValid()调用它?
4.在Spam类上创build一个is_valid静态方法,并用Spam.isValid(description,value)调用它?
5.检查setters声明的数据?
6.等等

你能推荐一个精心devise的/ Pythonic /不是详细的(在有很多属性的类)/优雅的方法吗?

您可以使用Python 属性将规则单独应用于每个字段,并在客户端代码尝试更改该字段时强制执行这些规则:

 class Spam(object): def __init__(self, description, value): self.description = description self.value = value @property def description(self): return self._description @description.setter def description(self, d): if not d: raise Exception("description cannot be empty") self._description = d @property def value(self): return self._value @value.setter def value(self, v): if not (v > 0): raise Exception("value must be greater than zero") self._value = v 

任何违反规则的尝试都会抛出exception,即使在__init__函数中,在这种情况下对象构造将会失败。

更新:在2010年和现在之间的某个时候,我学到了关于operator.attrgetter

 import operator class Spam(object): def __init__(self, description, value): self.description = description self.value = value description = property(operator.attrgetter('_description')) @description.setter def description(self, d): if not d: raise Exception("description cannot be empty") self._description = d value = property(operator.attrgetter('_value')) @value.setter def value(self, v): if not (v > 0): raise Exception("value must be greater than zero") self._value = v 

如果你只想在创build对象的时候validation这个值,并且传递一个无效的值被认为是一个编程错误,那么我会使用断言:

 class Spam(object): __init__(self, description, value): assert description != "" assert value > 0 self.description = description self.value = value 

这与你将要得到的一样简洁,明确地表明这些是创build对象的先决条件。

除非你自己动手,否则你可以简单地使用formencode 。 它真的闪耀着许多属性和模式(只是子类架构),并有许多有用的validation器内置。 正如你所看到的,这是“在创build垃圾邮件对象之前validation数据”的方法。

 from formencode import Schema, validators class SpamSchema(Schema): description = validators.String(not_empty=True) value = validators.Int(min=0) class Spam(object): def __init__(self, description, value): self.description = description self.value = value ## how you actually validate depends on your application def validate_input( cls, schema, **input): data = schema.to_python(input) # validate `input` dict with the schema return cls(**data) # it validated here, else there was an exception # returns a Spam object validate_input( Spam, SpamSchema, description='this works', value=5) # raises an exception with all the invalid fields validate_input( Spam, SpamSchema, description='', value=-1) 

你也可以在__init__进行检查(并且使它们对描述符|装饰器|元类完全透明),但我不是那么喜欢的。 我喜欢用户input和内部对象之间的干净屏障。

如果你只想validation传递给构造函数的值,你可以这样做:

 class Spam(object): def __init__(self, description, value): if not description or value <=0: raise ValueError self.description = description self.value = value 

这当然不会阻止任何人做这样的事情:

 >>> s = Spam('s', 5) >>> s.value = 0 >>> s.value 0 

所以,正确的方法取决于你想要完成什么。