Python在__new__方法中检查迭代

时间:2012-10-18 15:28:40

标签: python iterator

我正在尝试编写一个python(2.7)矩阵模块。 (我知道numpy,这只是为了好玩。)

我的代码:

from numbers import Number
import itertools

test2DMat = [[1,2,3],[4,5,6],[7,8,9]]
test3DMat = [[[1,2,3],[4,5,6],[7,8,9]],[[2,3,4],[5,6,7],[8,9,0]],[[9,8,7],[6,5,4],[3,2,1]]]

class Dim(list):
    def __new__(cls,inDim):
        # If every item in inDim is a number create a Vec
        if all(isinstance(item,Number) for item in inDim):
            #return Vec(inDim)
            return Vec.__new__(cls,inDim)

        # Otherwise create a Dim
        return list.__new__(cls,inDim)

    def __init__(self,inDim):
        # Make sure every item in inDim is iterable
        try:
            for item in inDim: iter(item)
        except TypeError:
            raise TypeError('All items in a Dim must be iterable')

        # Make sure every item in inDim has the same length
        # or that there are zero items in the list
        if len(set(len(item) for item in inDim)) > 1:
            raise ValueError('All lists in a Dim must be the same length')

        inDim = map(Dim,inDim)
        list.__init__(self,inDim)


class Vec(Dim):
    def __new__(cls,inDim):
        if cls.__name__ not in [Vec.__name__,Dim.__name__]:
            newMat = list.__new__(Vec,inDim)
            newMat.__init__(inDim)
            return newMat
        return list.__new__(Vec,inDim)

    def __init__(self,inDim):
        list.__init__(self,inDim)


class Matrix(Dim):
    def __new__(cls,inMat):
        return Dim.__new__(cls,inMat)

    def __init__(self,inMat):
        super(Matrix,self).__init__(inMat)

当前功能:

到目前为止,我已经写了几个课程,MatrixDimVecMatrixVec都是Dim的子类。在创建矩阵时,首先会从列表列表开始,然后创建一个矩阵,如:

>>> startingList = [[1,2,3],[4,5,6],[7,8,9]]
>>> matrix.Matrix(startingList)
[[1,2,3],[4,5,6],[7,8,9]]

这应创建一个Matrix。创建的Matrix应包含多个Dim的所有相同长度。这些Dim中的每一个都应该包含多个Dim的长度等等。最后Dim,包含数字的那个,应该只包含数字,应该是{{ 1}}而不是Vec

问题:

所有这些都适用于列表。但是,如果我使用迭代器对象(例如Dim返回的对象),则这不会像我想要的那样起作用。

例如:

iter()

我的想法:

我很确定这种情况正在发生,因为在>>> startingList = [[1,2,3],[4,5,6],[7,8,9]] >>> matrix.Matrix(iter(startingList)) [] 我迭代输入迭代,当相同的可迭代传递给Dim.__new__时,它已经被迭代过,因此会出现为空,导致我得到的空矩阵。

我尝试使用Matrix.__init__复制迭代器,但这也行不通,因为我实际上没有调用itertools.tee()Matrix.__init__返回时它会被隐式调用,因此我不能使用与传递给Matrix.__new__的参数不同的参数调用它。我想到的所有事情都会遇到同样的问题。

有没有办法让我保留现有的功能,还允许用迭代器对象调用Matrix.__init__

2 个答案:

答案 0 :(得分:3)

关键是Vec.__init__被称为两次;一旦进入__new__方法,一次从__new__方法返回。因此,如果您将其标记为已初始化并从Vec.__init__提前返回(如果已初始化),则可以忽略第二个调用:

class A(object):
    def __new__(cls, param):
        return B.__new__(cls, param + 100)
class B(A):
    def __new__(cls, param):
        b = object.__new__(B)
        b.__init__(param)
        return b
    def __init__(self, param):
        if hasattr(self, 'param'):
            print "skipping __init__", self
            return
        self.param = param
print A(5).param

答案 1 :(得分:0)

您需要做的是检查传入的变量是元组还是列表。如果是,那么你可以直接使用它,否则你需要将迭代器转换为list / tuple。

if isinstance(inDim, collections.Sequence):
    pass
elif hastattr(inDim, '__iter__'): # this is better than using iter()
    inDim = tuple(inDim)
else:
    # item is not iterable

还有一种更好的方法可以检查所有列表的长度是否相同:

if len(inDim) > 0:
    len_iter = (len(item) for item in inDim)
    first_len = len_iter.next()
    for other_len in len_iter:
        if other_len != first_len:
            raise ValueError('All lists in a Dim must be the same length')