Python inspect iterable in __new__ method - python

I'm trying to write a python (2.7) matrix module. (I know about numpy, this is just for fun.)
My Code:
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 a few classes, Matrix, Dim, and Vec. Matrix and Vec are both subclasses of Dim. When creating a matrix, one would first start out with a list of lists and they would create a matrix like:
>>> 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 should contain multiple Dims all of the same length. Each of these Dims should contain multiple Dims all of the same length, etc. The last Dim, the one that contains numbers, should contain only numbers and should be a Vec instead of a Dim.
The Problem:
All of this works, for lists. If I were however, to use an iterator object instead (such as that returned by iter()) this does not function as I want it to.
For example:
>>> startingList = [[1,2,3],[4,5,6],[7,8,9]]
>>> matrix.Matrix(iter(startingList))
[]
My Thoughts:
I'm fairly certain that this is happening because in Dim.__new__ I iterate over the input iterable which, when the same iterable is then passed to Matrix.__init__ it has already been iterated over and will therefore appear to be empty, resulting in the empty matrix that I get.
I have tried copying the iterator using itertools.tee(), but this also doesn't work because I don't actually call Matrix.__init__ it gets called implicitly when Matrix.__new__ returns and I therefore cannot call it with different parameters than those passed to Matrix.__init__. Everything I have thought of to do comes up against this same problem.
Is there any way for me to preserve the existing functionality and also allow matrix.Matrix() to be called with an iterator object?

The key is that Vec.__init__ is getting called twice; once inside your __new__ method and once when you return it from the __new__ method. So if you mark it as already initialised and return early from Vec.__init__ if it is already initialised, then 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

What you would need to do is check if the variable that is passed in is a tuple or list. If it is then you can use it directly, otherwise you need to convert the iterator into 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 of checking that the length of all the lists are 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')

Related

How to overload constructors in python?

Suppose I want to send a list in python and I will have a MyList class that will have 3 constructors. sending an empty list would return nothing, sending a list would convert it into a linked list and sending a linked list will copy its item and make another linkedlist from it
Task 1:
i) Create a Node class that will hold two fields i.e an integer element and a reference to the next Node.
ii) Create a Linked list Abstract Data Type (ADT)named MyList.The elements in the list are Nodes consisting of an integer type key (all keys are unique) and a reference to the next node.
Task 2:
Constructors:(3)
MyList ( )
Pre-condition: None.
Post-condition: This is the default constructor of MyList class. This constructor creates an empty list.
b. MyList (int [] a) or Myst(a)
Pre-condition: Array cannot be empty.
Post-condition: This is the default constructor of MyList class. This constructor creates a list from an array.
c. MyList (MyList a) or MyList(a)
Pre-condition: List cannot be empty.
Post-condition: This is the default constructor of MyList class. This constructor creates a list from another list.
For Task 2 : You can try something like this, by checking the input in the constructor of your class:
class MyList():
def __init__(self, a=None):
self.list = []
if a is None:
return
elif isinstance(a, list):
if len(a) == 0:
raise ValueError('List cannot be empty.')
else:
self.list = a
return
elif isinstance(a, MyList):
if len(a.list) == 0:
raise ValueError('List cannot be empty.')
else:
self.list = a.list.copy()
else:
raise TypeError('Unkonw type for a')

Python: is it possible to return a variable amount of variables as requested?

I'd like to return either one or two variables for a function in python(3.x). Ideally, that would depend on amount of returned variables requested by user on function call. For example, the max() function returns by default the max value and can return the argmax. In python, that would look something like:
maximum = max(array)
maximum, index = max(array)
Im currently solving this with an extra argument return_arg:
import numpy as np
def my_max(array, return_arg=False):
maximum = np.max(array)
if return_arg:
index = np.argmax(array)
return maximum, index
else:
return maximum
This way, the first block of code would look like this:
maximum = my_max(array)
maximum, index = my_max(array, return_arg=True)
Is there a way to avoid the extra argument? Maybe testing for how many vaules the user is expecting? I know you can return a tuple and unpack it when calling it (that's what I'm doing).
Asume the actual function I'm doing this in is one where this behaviour makes sense.
You can instead return an instance of a subclass of int (or float, depending on the data type you want to process) that has an additional index attribute and would return an iterator of two items when used in a sequence context:
class int_with_index(int):
def __new__(cls, value, index):
return super(int_with_index, cls).__new__(cls, value)
def __init__(self, value, index):
super().__init__()
self.index = index
def __iter__(self):
return iter((self, self.index))
def my_max(array, return_arg=False):
maximum = np.max(array)
index = np.argmax(array)
return int_with_index(maximum, index)
so that:
maximum = my_max(array)
maximum, index = my_max(array)
would both work as intended.
The answer is no, in Python a function has no context of the caller and can't know how many values the caller expects in return.
Instead in Python you would rather have different functions, a flag in the function signature (like you did) or you would return an object with multiple fields of which the consumer can take whatever it needs.
No, there is no way of doing this. my_max(array) will be called and return before assigning a value to maximum. If more than one value is returned by the function then it will try unpacking the values and assigning them accordingly.
Most people tackle this problem by doing this:
maximum, _ = my_max(array)
maximum, index = my_max(array)
or
maximum = my_max(array)[0]
maximum, index = my_max(array)
If you need a solution that works for any data type such as np.ndarray, you can use a decorator that uses ast.NodeTransformer to modify any assignment statement that assigns a call to a given target function name (e.g. my_max) to a single variable name, to the same statement but assigns to a tuple of the same variable name plus a _ variable (which by convention stores a discarded value), so that a statement such as maximum = my_max(array) is automatically transformed into maximum, _ = my_max(array):
import ast
import inspect
from textwrap import dedent
class ForceUnpack(ast.NodeTransformer):
def __init__(self, target_name):
self.target = ast.dump(ast.parse(target_name, mode='eval').body)
def visit_Assign(self, node):
if isinstance(node.value, ast.Call) and ast.dump(node.value.func) == self.target and isinstance(node.targets[0], ast.Name):
node.targets[0] = ast.Tuple(elts=[node.targets[0], ast.Name(id='_', ctx=ast.Store())], ctx=ast.Store())
return node
# remove force_unpack from the decorator list to avoid re-decorating during exec
def visit_FunctionDef(self, node):
node.decorator_list = [
decorator for decorator in node.decorator_list
if not isinstance(decorator, ast.Call) or decorator.func.id != "force_unpack"
]
self.generic_visit(node)
return node
def force_unpack(target_name):
def decorator(func):
tree = ForceUnpack(target_name).visit(ast.parse(dedent(inspect.getsource(func))))
ast.fix_missing_locations(tree)
scope = {}
exec(compile(tree, inspect.getfile(func), "exec"), func.__globals__, scope)
return scope[func.__name__]
return decorator
so that you can define your my_max function to always return a tuple:
def my_max(array, return_arg=False):
maximum = np.max(array)
index = np.argmax(array)
return maximum, index
while applying the force_unpack decorator to any function that calls my_max so that the assignment statements within can unpack the returning values of my_max even if they're assigned to a single variable:
#force_unpack('my_max')
def foo():
maximum = my_max(array)
maximum, index = my_max(array)

Calling a class for testing - Python

First off this is a homework assignment I'm working on, but I really just need help on an error.
So the project is to implement a vector (a list in all but name for this project), using the Array class. The array class I'm using can be found here.
My error is that every time I try to call my code to test it, specifically the getitem and setitem functions, I wind up with an error stating:
builtins.TypeError: 'type' object does not support item assignment
Below is the class I'm currently building, (so far it seems that only len and contains are working).
class Vector:
"""Vector ADT
Creates a mutable sequence type that is similar to Python's list type."""
def __init__(self):
"""Constructs a new empty vector with initial capacity of two elements"""
self._vector = Array(2)
self._capacity = 2
self._len = 0
def __len__(self):
"""Returns the number of items in the vector"""
return self._len
def __contains__(self, item):
"""Determines if the given item is stored in the vector"""
if item in self._vector:
return True
else:
return False
def __getitem__(self, ndx):
"""Returns the item in the index element of the list, must be within the
valid range"""
assert ndx >= 0 and ndx <= self._capacity - 1, "Array subscript out of range"
return self._vector[ndx]
def __setitem__(self, ndx, item):
"""Sets the elements at position index to contain the given item. The
value of index must be within a valid range"""
assert ndx >= 0 and ndx <= self._capacity - 1, "Array subscript out of range"
self._vector[ndx] = item
def append(self, item):
"""Adds the given item to the list"""
if self._len < self._capacity:
self._vector[self._len] = item
self._len += 1
I'm trying to call the code by either typing:
Vector()[i] = item
or
Vector[i] = item
However, trying:
Vector[i] = item
Gives me the error, and:
Vector()[i] = item
Doesn't really seem to do anything other than not cause an error.
You need to create an instance of your Vector class. Try:
vector = Vector()
vector[0] = 42
The error means that you are trying erroneously to assign to the Vector class itself, which does not make much sense.
Try using the replace method instead of assigning a value.
Vector is a class; Vector() creates an instance of that class.
So
Vector[i] = item
gives an error: Vector.__setitem__ is an instance method (runs against an instance of a class, ie an object), not a classmethod (runs against a class). (You could in theory make it a classmethod, but I have trouble picturing a use case where that would make sense.)
On the other hand,
Vector()[i] = item
# 1. creates a Vector() object
# 2. calls {new_object}.__setitem__(self, i, item)
# 3. doesn't keep any reference to {new_object}, so
# (a) you have no way to interact with it any more and
# (b) it will be garbage-collected shortly.
Try
v = Vector()
v[i] = item
print(item in v) # => True

using __class__ to change python class, Heap Error

I am trying to make an class = that extends from list return a slice of itself instead of a list type. The reason I want to do this is because I have many other methods to manipulate the instance of A.
I am running python 2.7.3
Say I have:
class B():
def __init__(self, t, p):
self.t = t
self.p = p
class Alist(list):
def __init__(self, a_list_of_times = []):
for a_time in a_list_of_times:
self.append(a_time )
def __getslice__(self, i, j):
return super(Alist, self).__getslice__(i,j)
def plot_me(self):
pass
# other code goes here!
alist1 = Alist()
for i in range(0, 1000000):
alist1.append(B(i, i)) # yes ten million, very large list!
alist = alist1[1000:200000] # will return a list!
alist2 = Alist(alist) # will return Alist istance
The problem is that remaking the entire list as seen in making variable b is VERY VERY SLOW (comparative to the slice). What I want to do is simply change the class of alist (currently of type list)to Alist
When I try:
alist.__class__ = Alist
>>>> TypeError: __class__ assignment: only for heap types.
Which is very sad since I can do this for my own object types.
I understand that it is not standard, but it is done.
Reclassing an instance in Python.
Is there a way around this? Also I have obviously simplified the problem, where my objects a bit more complex. Mainly what I am finding is that remaking the list into my Alist version is slow. And I need to do this operation a lot (unavoidable). Is there a way to remake A? or a solution to this to make it speed up?
In my version, I can do about a 10,000 (size of my slice) slice in 0.07 seconds, and converting it to my version of Alist takes 3 seconds.
The UserList class (moved to collections in Python 3) is perfectly designed for this. It is a list by all other means but has a data attribute that you can store an underlying list in without copying.
from UserList import UserList
class Alist(UserList):
def __init__(self, iterable, copy=True):
if copy:
super(Alist, self).__init__(iterable)
else:
self.data = iterable
def plot_me(self):
pass

Why won't recursive generator work?

I have a class where each instance is basically of a bunch of nested lists, each
of which holds a number of integers or another list containing integers, or a
list of lists, etc., like so:
class Foo(list):
def __init__(self):
self.extend(
list(1), list(2), list(3), range(5), [range(3), range(2)]
)
I want to define a method to walk the nested lists and give me
one integer at a time, not unlike os.walk. I tried this:
def _walk(self):
def kids(node):
for x in node:
try:
for y in kids(x):
yield y
except TypeError:
yield x
return kids(x)
But it immediately raises a stopiteration error. If I add a print statement to print each "node" in the first for loop, the function appears to iterate over the whole container in the way I want, but without yielding each node. It just prints them all the first time I call next on the generator.
I'm stumped. Please help!
It works if you change return kids(x) to return kids(self)
Here's a function that is a simpler version of your _walk method that does what you want on an arbitrary iterable. The internal kids function is not required.
def walk(xs):
for x in xs:
try:
for y in walk(x):
yield y
except TypeError:
yield x
This could be trivially adapted to work as a method on your Foo object.

Categories