用于类实例变量的对象方法链接的Python方法

时间:2012-12-12 18:25:19

标签: python python-2.7

用例:

我有一个表单对象,它接收实例变量(表单字段)。现在我想使用方法链来验证变量。使用任意方法的示例:

class Field(object):

    def __init__(self, form, name):
        self.form = form
        self.name = name

    def unspace(self):
        setattr(self.form, self.name, getattr(self.form, self.name).replace(' ',''))
        return self

    def len_valid(self, length):
        if len(getattr(self.form, self.name)) < length :
            setattr(self.form, self.name + '_invalid', True)
            self.form.valid = False
        return self

class Forms(object):                                                                                    

    def __init__(self):
        self.valid = True

    def validate(self, name):
        return Field(self,name)

f = Forms()         # create the form with some data
f.a = 'J o Hn '
f.b = ' Too   L o n g'

f.validate('a').unspace().len_valid(2)  
f.validate('b').unspace().len_valid(5)  

RESULT :
f.a : 'JoHn'
f.a_invalid : True
f.b : 'TooLong'
f.valid : False

这是Pythonic在Form实例变量上创建方法链的方法吗?

1 个答案:

答案 0 :(得分:4)

是和否。

链式方法调用的Pythonic方法正是你所写的:

f.validate('a').unspace().len_valid(2)  

但是动态访问属性的Pythonic方法是不要这样做,除非你必须这样做。如果表单变量存储在dict而不是对象的实例变量中,那么一切都会更简单,更易读。

即使您确实需要将表单变量作为f.a而不是f['a']进行访问(例如,因为这是交互式shell的一部分,或者某些第三方API需要),所以实际上更容易在dict周围编写所有代码,并使用您最喜欢的AttrDict类(来自PyPI或ActiveState)为您的用户/第三方API提供属性样式访问。

此外,如果您使用某些方法进一步将Field对象更改为值的简单包装,而不是(实际上)对父级和键的引用,则更简单。 / p>

此外,如果您像a_invalid那样动态生成新属性,您可能希望始终生成它们,而不仅仅是在它们为真时。否则,检查a是否有效看起来像这样:

try:
    avalid = not f.a_invalid
except NameError:
    avalid = True

这是非常令人费解的,但如果你的来电者想要避免这种情况,那么这样做的唯一方法就是这样:

avalid = not getattr(f, 'a_invalid', False)

这似乎首先打败了为调用者伪造属性的整个目的。

另外,请记住,您必须确保永远不会有名称以_invalid结尾的字段。既然您可以将新属性附加到Python中的任何内容中,那么如果您确实希望以这种方式执行所有操作,那么为什么要使用f.a_invalid而不是f.a.invalid

由于您在评论中提到,值周围的值很简单:

class Field(object):

    def __init__(self, value):
        self.value = value
        self.valid = True

    def unspace(self):
        self.value = self.value.replace(' ', '')
        return self

    def len_valid(self, length):
        if len(self.value) < length:
            self.valid = False
        return self

不要让每个字段都达到表单来设置其有效性,只需让表单执行:

class Form(object):
    …
    def valid(self):
        return all(field.valid for field in self.fields)

如果你真的需要让valid看起来像一个成员变量而不是一个方法,那就使用@property