Python中类似于Pojo的类

9

我希望在Python中创建一个类似于User的Pojo。每个属性都将涉及一些验证。例如:我不希望有人使用负值年龄创建用户。为了实现这一点,我最终会编写类似以下代码的类。

 class User:
        def __init__(self, age):
            self.age = age

        @property
        def age(self):
            return self._age

        @age.setter
        def age(self, value):
            print("Called")
            if value >= 0:
                self._age = 0
            else:
                raise ValueError("Age can't be negative")

看到上面的类后,我有点害怕。原因如下:

  1. 我的用户类将具有大约50个属性。在这种情况下,我有点害怕想象这个类会有多大。

这是在Python中创建此类的正确方式吗?


3
构造函数中 self.age = age 就足够了。 - Klaus D.
需要做的事情是定义自己的正整数类型,但在Python中定义新类型并不简单。 - Red Cricket
1
如果你真的需要验证类中的50个字段,我建议你考虑一些DSL(领域特定语言)的方法来完成这个任务。一种方法是将这些字段声明为列表,例如[('age', 'int', lambda x: x >= 0), ...],其中第一个元素是字段名称,第二个是类型,第三个是验证器,然后在类中正确处理__getattr__、比较器等。另一种方法是像Django模型一样声明字段,但实现起来可能更困难。尽管你可能会失去IDE为属性提供的自动完成支持,但你将大大减少重复的代码。 - awesoon
@soon 在这个上下文中,DSL 是什么意思?你能否举个例子来详细解释一下,作为对这个问题的回答吗? - ThinkGeek
可能是重复的问题:Python: 如何实现 __getattr__()? - stovfl
显示剩余7条评论
1个回答

13
我不认为这有什么笨拙的地方——大多数语言并没有内置的getter/setter验证,因此无论您选择哪种语言,最终都需要编写类似于此的代码。如果您想将所有属性转换为具有验证的getter/setter对,那么您必须分别编写它们。
现在,Python是一种非常灵活的动态语言,因此有几种方法可以减少此过程的冗长性(但不能减少其复杂性)。例如,您可以创建自己的验证器装饰器来覆盖您的setter,例如:
def validator(cmp, exc):
    def decorator(setter):  # a decorator for the setter
        def wrapper(self, value):  # the wrapper around your setter
            if cmp(value):  # if cmp function returns True, raise the passed exception
                raise exc
            setter(self, value)  # otherwise just call the setter to do its job
        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类,该类允许您传递验证范围,然后在需要时重复使用)。

另一种选择是把您的类的用户视为成年人,并在文档中解释有效值,如果他们超出了范围,则有风险。如果该类旨在由最终用户填充数据,则应在收集最终用户数据的一侧执行验证(以便最终用户可以获得针对他们的有意义的错误),而不一定在模型本身中执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接