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
Related
I was working on the sorting but I'm not able to call the function with the specific way.
Basically, what I want to do is to create a function that takes a list of object Node with attribute Value and returns a list with the items from the original list stored into sublists. Items of the same value should be in the same sublist and sorted in descending order.
For continuing the code I want to know what should be the parameter of this.
def advanced_sort(<What will come here according to the call>):
Function call:
advanced_sort([Node(1), Node(2), Node(1),Node(2)])
Can anyone please help me out with the code? Thanks in advance.
advanced_sort takes a single argument: a list (or possibly an arbitrary iterable). As such, the signature only has one argument:
def advanced_sort(nodes):
Ignoring type hints, the signature does not and cannot reflect the internal structure of the single argument; it's just a name to refer to the passed value inside the body of the function.
Inside the body, you can write code that assumes that nodes is a list, and that further each element of the list is a Node instance, so that you can do things like assume each value as a Value attribute.
def advanced_sort(nodes):
# If nodes is iterable, then x refers to a different
# element of the iterable each time through the loop.
for x in nodes:
# If nodes is a list of Node instances, then
# x is a Node instance, and thus you can access
# its Value attribute in the normal fashion.
print("Found value {}".format(x.Value))
Assuming a definition of Node like
class Node:
def __init__(self, v):
self.Value = v
the above definition of advanced_sort will produce the following output:
>>> advanced_sort([Node(3), Node(2), Node(1),Node(2)])
Found value 1
Found value 2
Found value 3
Found value 4
The argument is a single iterable object such as a list, a tuple, a set, ...
Then you iterate on the items as in chepner's response.
For exemple you can use a dictionary to group the Nodes by value:
def advanced_sort(node_list):
ret = dict()
for node in node_list:
if node.value not in ret.keys():
ret[node.value] = list()
ret[node.value].append(node)
return [ret[value] for value in sorted(ret.keys(), reverse=True)] #descending order
advanced_sort([Node(3), Node(2), Node(1),Node(1)])
>>> [[Node(3)], [Node(2)], [Node(1),Node(1)]]
Are you able to make changes to the Node class? In that case, you could do something like this:
from functools import total_ordering
#total_ordering
class Node:
def __init__(self, value):
self.value = value
def __eq__(self, other):
if not isinstance(other, Node):
return NotImplemented
return self.value == other.value
def __lt__(self, other):
if not isinstance(other, Node):
return NotImplemented
return self.value < other.value
def __str__(self):
return f"({self.value})"
def main():
from itertools import groupby
nodes = [Node(1), Node(2), Node(1), Node(2)]
nodes_sorted = sorted(nodes, reverse=True)
nodes_sublists = [list(group) for key, group in groupby(nodes_sorted)]
for sublist in nodes_sublists:
print(*map(str, sublist))
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
Output:
(2) (2)
(1) (1)
Consider the following code:
class A(object):
def __init__(self):
self.a = '123'
def __len__(self):
print('len')
return 2
def __getitem__(self, pos):
print('get pos', pos)
return self.a[pos]
a = A()
print(''.join(a))
My expected output:
> len
> get pos 0
> get pos 1
> 12
The real output:
> len
> get pos 0
> get pos 1
> get pos 2
> get pos 3
> 123
Try it your self. I cannot believe what happens here.
As I understand the behavior correctly, str.join() calls __len__ but ignores the value and calls __getItem__ until the index out of range exception.
I must overlook something because the implementation of join seems different:
https://github.com/python/cpython/blob/3.6/Objects/stringlib/join.h
My current workaround is:
def __getitem__(self, pos):
if pos >= len(self):
raise IndexError()
return self.a[pos]
This is ridiculous.
I tested it under with Python 3.6 and 3.7 (CPython).
How str.join works (from analysing the source code)
First it checks if the object is an iterable & creates a sequence out of it if needed
seq = PySequence_Fast(iterable, "can only join an iterable");
If the object is a list or tuple, it just returns the object itself, no need to iterate.
If it's not, then it iterates to create a list. That's where the object is fully iterated upon.
From there, only the list copy is used. iterable has been iterated upon and is useless now if it wasn't list or tuple.
(I couldn't track down the call to len, would take a debugging session to find it in the PySequence_Fast call, but that seems useless. Your iterable has a __len__ method, okay, but since it's not a list or tuple, the returned value isn't used)
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)
Suppose I convert the below pseudocode to Python. Regarding specifically the parameter indicated as 1st half of A, does Python have a mechanism like A[1..n/2] (another pseudocode shortcut I see from time to time) that does not require copy for passing part of a list as a parameter ?
Count(array A, length n)
if n = 1 return 0
else
x = Count(1st half of A, n/2)
y = Count(2nd half of A, n/2)
return x + y
Without such a mechanism I will pass indices as necessary.
The answer is no. You'll have to pass indices (or slice objects).
You could also write a list subclass that handles slices by returning "views" into the original list rather than copies. I've actually tackled this a few times and found it tricky to get completely right, but it's made much easier by the fact that your application doesn't need negative indexing, slice assignment, or the skip parameter. Here's a quick try:
class ListWithAView(list):
class ListView(object):
def __init__(self, list, start, stop, step):
self.list = list
self.start = start
self.stop = stop
self.step = step
def __iter__(self):
for i in xrange(self.start, self.stop, self.step):
yield self.list[i]
def __len__(self):
return (self.stop - self.start) / self.step
def __getitem__(self, i):
if isinstance(i, slice):
return type(self)(self.list, (i.start or 0) + self.start,
min(self.start + (i.stop or 0), self.stop),
i.step * self.step if i.step else self.step)
if isinstance(i, int) and i < len(self):
return self.list[i+self.start]
raise IndexError("invalid index: %r" % i)
def __setitem__(self, i, v):
if isinstance(i, int):
self.list[i+self.start] = v
else:
raise IndexError("invalid index: %r" % i)
def __repr__(self):
return "<slice [%s:%s:%s] of list id 0x%08x>: %s" % (self.start, self.stop, self.step, id(self.list), self)
def __str__(self):
return str(list(self))
__str__ = __repr__
#property
def view(self):
return self.ListView(self, 0, len(self), 1)
The view property of this list subclass returns a ListView object that acts much like a list, but gets and sets the data in the underlying list rather than storing any items itself. The returned object initially refers to the entire list but can be sliced further if desired. For simplicity, negative steps aren't handled, and you can't do slice assignment, just single items.
Quick demo:
seq = ListViwthAView(range(100))
view = seq.view[10:20][5:7]
view[0] = 1337
print seq[15] # 1337
You can sort of use slice objects here, but unfortunately there isn't a __len__ method, so you have to use (s.start + s.stop)/2 to compute the length. Any time you wise to "materialise" the subarray (which of course creates a copy), you can use A[s]
def count(A, s=None):
if s is None:
s=slice(0, len(A))
if s.start + 1 == s.stop:
return 1
else:
x = count(A, slice(s.start, (s.start + s.stop)/2))
y = count(A, slice((s.start + s.stop)/2, s.stop))
return x + y
print count([1,2,3,4,5])
In your example, the best solution is to just pass the list and the indices as you suggested.
If you didn't need to index into the slices (for example, if just having iterators over the first and second halves of the list was sufficient), you could use the islice function from itertools. E.g.
from itertools import islice
half = (len(sequence) + 1) // 2
first_half = islice(sequence, half):
second_half = islice(sequence, half, len(sequence))
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')