打字列表是unpythonic吗?

时间:2015-07-04 09:50:41

标签: python properties

我经常遇到这个问题而且我从来不知道最好的解决方法是什么。

假设我有一个类,其中一个属性是列表。该列表存储在内部,而不是直接访问列表,而是要求用户通过类提供的一组方法与列表进行交互。考虑下面的代码示例:

from validation.check import Check


class Validator(object):
    """
    Class used to perform validation.
    """

    def __init__(self):
        self._checks = []

    def add_check(self, check):
        """
        Add the given check to the validator.

        :param check: Check instance.
        :type check: :class:`validation.check.Check`

        :raises TypeError: When given value is not a validation.Check
        """
        if not isinstance(check, Check):
            msg = 'Check item must be type "{0}". Got "{1}"'
            msg = msg.format(Check.__name__, type(check).__name__)
            raise TypeError(msg)

        self._checks.append(check)

    @property
    def checks(self):
        """
        Get validator checks.
        """
        return self._checks

在这种情况下,add_check方法会在将值添加到列表之前对值进行类型检查,但您可以在add_check方法中添加所有类型的额外功能。

checks属性允许用户检查检查列表。但是,它还允许用户在不使用add_check方法的情况下修改检查列表。例如:

validator = Validator()
validator.checks.append('BogusValue')

现在我的checks列表中的值无效,因为绕过了add_check方法。什么是解决这个问题的最好,也是最蟒蛇的方式?

有些人建议退回内部列表的副本。

    @property
    def checks(self):
        """
        Get validator checks.
        """
        return list(self._checks)

在这种情况下,修改副本不会影响checks内部存储的Validator列表。我可以看到这是如何解决问题的,但我认为在某些情况下可能会引起混淆。

有些人建议您返回tuple或创建只读列表:

import collections


class ReadOnlyList(collections.Sequence):
    def __init__(self, items):
        super(ReadOnlyList, self).__init__()
        self._items = items

    def __getitem__(self, index):
        return self._items[index]

    def __len__(self):
        return len(self._items)

Validator.checks属性返回只读列表会阻止用户直接修改checks列表。

    def checks(self):
        """
        Get validator checks.
        """
        return ReadOnlyList(self._checks)

如果我遇到创建自定义列表对象的麻烦,为什么不将类型检查构建到自定义列表对象中?例如:

import collections


class Checks(collections.MutableSequence):

    def __init__(self, items):
        self._items = []
        for index, item in enumerate(items):
            self.insert(index, item)

    def _validate_item(self, item):
        if not isinstance(item, Check):
            msg = 'List item must be type "{0}". Got "{1}"'
            msg = msg.format(Check.__name__, type(item).__name__)
            raise TypeError(msg)

    def __getitem__(self, index):
        return self._items[index]

    def __setitem__(self, index, item):
        self._validate_item(item)
        self._items[index] = item

    def __delitem__(self, index):
        del self._items[index]

    def __len__(self):
        return len(self._items)

    def insert(self, index, item):
        self._validate_item(item)
        self._items.insert(index, item)

然后我可以完全删除add_check方法,因为类型检查是内置于Checks对象的。

class Validator(object):
    """
    Class used to perform validation.
    """

    def __init__(self):
        self._checks = Checks()

    @property
    def checks(self):
        """
        Get validator checks.
        """
        return self._checks

最后,有些人认为上述所有内容都是过度的,而且文档齐全的代码应该足以阻止用户以非预期的方式添加检查。

也许它们都是有效的解决方案?在这种情况下,我在选择方法并坚持下去时遇到了很多麻烦。如果有人对这个问题有强烈的感受,我很乐意听到你的想法。

非常感谢。

0 个答案:

没有答案