2

用例:

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

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

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

4

1 回答 1

4

是和不是。

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

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

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

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

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

此外,如果您像 一样动态生成新属性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.

于 2012-12-12T18:32:46.230 回答