我不认为这有什么笨拙的地方——大多数语言并没有内置的getter/setter验证,因此无论您选择哪种语言,最终都需要编写类似于此的代码。如果您想将所有属性转换为具有验证的getter/setter对,那么您必须分别编写它们。
现在,Python是一种非常灵活的动态语言,因此有几种方法可以减少此过程的冗长性(但不能减少其复杂性)。例如,您可以创建自己的验证器装饰器来覆盖您的setter,例如:
def validator(cmp, exc):
def decorator(setter):
def wrapper(self, value):
if cmp(value):
raise exc
setter(self, value)
return wrapper
return decorator
现在,您可以定义带有验证的getter/setter对,如下所示:
class User:
def __init__(self, age):
self.age = age
@property
def age(self):
return self._age
@age.setter
@validator(lambda x: x < 0, ValueError("Age can't be negative"))
def age(self, value):
self._age = value
然而,如果你只需要在setters(和getters)中进行验证而不进行其他处理,那么你可以定义自己的validating属性,从而大大减少冗余代码,例如:
class ValidatingProperty(object):
def __init__(self, prop, cmp, exc):
self.prop = prop
self.cmp = cmp
self.exc = exc
def __get__(self, instance, owner=None):
if instance is None:
return self
return getattr(instance, self.prop, None)
def __set__(self, instance, value):
if self.cmp(value):
raise self.exc
setattr(instance, self.prop, value)
def __delete__(self, instance):
delattr(instance, self.prop)
现在你可以像这样简单地构建你的类 getters/setters:
class User:
age = ValidatingProperty("_age", lambda x: x < 0, ValueError("Age can't be negative"))
def __init__(self, age):
self.age = age
如果您需要访问原始属性(假设已设置),而没有包装器(wrappers)围绕它,您仍然可以使用self._age
(或者您作为ValidatingProperty
的第一个参数传递的“真实”属性)。您甚至可以单独构建验证器,以便不依赖于lambda表达式(例如,创建一个IntegerValidator
类,该类允许您传递验证范围,然后在需要时重复使用)。
另一种选择是把您的类的用户视为成年人,并在文档中解释有效值,如果他们超出了范围,则有风险。如果该类旨在由最终用户填充数据,则应在收集最终用户数据的一侧执行验证(以便最终用户可以获得针对他们的有意义的错误),而不一定在模型本身中执行。
self.age = age
就足够了。 - Klaus D.[('age', 'int', lambda x: x >= 0), ...]
,其中第一个元素是字段名称,第二个是类型,第三个是验证器,然后在类中正确处理__getattr__
、比较器等。另一种方法是像Django模型一样声明字段,但实现起来可能更困难。尽管你可能会失去IDE为属性提供的自动完成支持,但你将大大减少重复的代码。 - awesoon