4

我想要一个 python 列表,它在外部将自身表示为其内部列表项的平均值,但在其他方面表现为列表。TypeError如果添加的项目不能转换为浮点数,它应该引发 a 。

我坚持的部分是 raise TypeError。应该为通过任何列表方法添加的无效项目引发它,例如.append, .extend, +=, 切片设置等。

有没有办法拦截添加到列表中的新项目并验证它们?

我尝试在 中重新验证整个列表__getattribute__,但是当它被调用时,我只能访问旧版本的列表,而且它甚至不会被称为初始化、运算符(如+=)或切片(如mylist[0] = 5.

有任何想法吗?

4

6 回答 6

3

MutableSequence继承并实现它所需的方法以及任何其他超出 Sequences 范围的方法,例如此处的运算符。这将允许您更改类似列表功能的运算符操作,同时自动生成迭代器和包含功能。

如果您想检查切片顺便说一句,您需要isinstance(key, slice)在您的__getitem__(and/or __setitem__) 方法中执行。请注意,像 myList[0] 这样的单个索引不是切片请求,而是单个索引,而 myList[:0] 是实际的切片请求。

于 2012-08-30T17:04:20.977 回答
2

array.array课程将负责以下float部分:

class AverageList(array.array):
    def __new__(cls, *args, **kw):
        return array.array.__new__(cls, 'd')
    def __init__(self, values=()):
        self.extend(values)
    def __repr__(self):
        if not len(self): return 'Empty'
        return repr(math.fsum(self)/len(self))

还有一些测试:

>>> s = AverageList([1,2])
>>> s
1.5
>>> s.append(9)
>>> s
4.0
>>> s.extend('lol')
Traceback (most recent call last):
  File "<pyshell#117>", line 1, in <module>
    s.extend('lol')
TypeError: a float is required
于 2012-08-30T16:57:05.030 回答
1

该类有 7 个方法list可以将元素添加到列表中,并且必须进行检查。这是一个紧凑的实现:

def check_float(x):
    try:
        f = float(x)
    except:
        raise TypeError("Cannot add %s to AverageList" % str(x))

def modify_method(f, which_arg=0, takes_list=False):
    def new_f(*args):
        if takes_list:
            map(check_float, args[which_arg + 1])
        else:
            check_float(args[which_arg + 1])
        return f(*args)
    return new_f

class AverageList(list):
    def __check_float(self, x):
        try:
            f = float(x)
        except:
            raise TypeError("Cannot add %s to AverageList" % str(x))

    append = modify_method(list.append)
    extend = modify_method(list.extend, takes_list=True)
    insert = modify_method(list.insert, 1)
    __add__ = modify_method(list.__add__, takes_list=True)
    __iadd__ = modify_method(list.__iadd__, takes_list=True)
    __setitem__ = modify_method(list.__setitem__, 1)
    __setslice__ = modify_method(list.__setslice__, 2, takes_list=True)
于 2012-08-30T17:10:42.017 回答
1

实际上最好的答案可能是:不要。

在将所有对象添加到列表时对其进行检查将在计算上很昂贵。做这些检查你有什么收获?在我看来,您获得的收益很少,我建议您不要实施它。

Python 不检查类型,因此尝试对一个对象进行一点类型检查确实没有多大意义。

于 2012-08-30T17:16:57.370 回答
0

一般的方法是创建您自己的类继承 vom 列表并覆盖特定方法,如 append、extend 等。这可能还包括 Python 列表的魔术方法(有关详细信息,请参阅本文:http://www.rafekettler。 com/magicmethods.html#sequence)。

为了验证,您需要覆盖 __setitem__(self, key, value)

于 2012-08-30T17:00:03.003 回答
0

MutableSequence以下是如何使用模块中的抽象基类作为其基类来创建子collections类(未完全测试——读者练习;-):

import collections

class AveragedSequence(collections.MutableSequence):
    def _validate(self, x):
        try: return float(x)
        except: raise TypeError("Can't add {} to AveragedSequence".format(x))
    def average(self):  return sum(self._list) / len(self._list)
    def __init__(self, arg):  self._list = [self._validate(v) for v in arg]
    def __repr__(self):  return 'AveragedSequence({!r})'.format(self._list)
    def __setitem__(self, i, value):  self._list[i] = self._validate(value)
    def __delitem__(self, i):  del self._list[i]
    def insert(i, value):  return self._list.insert(i, self._validate(value))
    def __getitem__(self, i):  return self._list[i]
    def __len__(self):  return len(self._list)
    def __iter__(self):  return iter(self._list)
    def __contains__(self, item):  return item in self._list

if __name__ == '__main__':
    avgseq = AveragedSequence(range(10))
    print avgseq
    print avgseq.average()
    avgseq[2] = 3
    print avgseq
    print avgseq.average()
    # ..etc
于 2012-08-30T19:10:35.837 回答