Python checks iterable in __new__ method

I am trying to write a python matrix module (2.7). (I know about numpy, this is just for fun.)

My code is:

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) 

Current Functionality:

So far I have written several classes, Matrix , Dim and Vec . Matrix and Vec are both subclasses of Dim . When creating a matrix, you would first need to make a list of lists, and they would create a matrix such as:

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

This should create a Matrix . The created Matrix must contain several Dim all of the same length. Each of these Dim must contain several Dim all of the same length, etc. The last Dim that contains numbers should only contain numbers and should be Vec instead of Dim .

Problem:

All this works for lists. If I, however, would instead use an iterator object (e.g. returned by iter() ), this does not work the way I want.

For instance:

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

My thoughts:

I'm sure this happens because in Dim.__new__ I repeat the iteration of input, which, when the same number is then passed to Matrix.__init__ , is already renamed and therefore will be empty, resulting in an empty matrix.

I tried copying the iterator using itertools.tee() , but this also does not work, because I do not actually call Matrix.__init__ , it gets an implicit expression when Matrix.__new__ returned, and therefore I can not name it with different parameters than those passed to Matrix.__init__ . All that I came up with is the same problem.

Is there a way to preserve the existing functionality and also allow the call of matrix.Matrix() with an iterator object?

+4
source share
2 answers

The key is that Vec.__init__ receives the call twice; once inside your __new__ method and once when you return it from the __new__ method. Therefore, if you marked it as already initialized and returned earlier from Vec.__init__ , if it is already initialized, you can ignore the second call:

 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 
+3
source

What you need to do is check if the variable that is being passed is a tuple or a list. If so, you can use it directly, otherwise you need to convert the iterator to a 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 

There is also a better way to verify that the length of all lists is the same:

 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') 
0
source

Source: https://habr.com/ru/post/1440580/


All Articles