Class iteration over internal numpy array ? Assignment? - python

How do you make class to iterate by its internal numpy array :
Just idea which does not work :
class ABC:
def __init__(self):
self.ary = np.zeros(50)
def __iter__(self): return np.nditer(self.ary)
def next(self): ...??..
Also how to make assignment work too :
abc = ABC()
abc[5] = 12
abc[7:9] 0

From documentation,
iterator.__next__():
Return the next item from the container. If there are no further items, raise the StopIteration exception. This method corresponds to the tp_iternext slot of the type structure for Python objects in the Python/C API.
For setting and getting values for container class, you need to implement __getitem__ and __setitem__.
For your sample code
class ABC():
def __init__(self):
self.ary = np.zeros(50)
self.index = self.ary.shape[0]
def __iter__(self):
return np.nditer(self.ary)
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
def _check_indx(self, idx):
if abs(idx) >= self.ary.shape[0]:
raise IndexError(f"Invalid Index {idx} for array with shape {self.ary.shape}")
def __setitem__(self, idx, value):
self._check_indx(idx)
self.ary[idx] = value
def __getitem__(self, idx):
self._check_indx(idx)
return self.ary[idx]

Related

Python class does not have value

I am a Javascript engineer and am switching into a JS/Python role. Working on some easy leetcodes to get some quick Python practice.
I'm looking to create a LinkedList here and perhaps I am coming at it from a JS mindset?
Error:
AttributeError: type object 'LinkedListNode' has no attribute 'value'
utils.py
# LinkedList Methods
def createLinkedList(arr):
head = createLinkedListNode(None, arr.pop(0))
def populateList(arr, prevNode):
if arr:
node = createLinkedListNode(None, arr.pop(0))
prevNode.next = node
if arr:
populateList(arr, node)
populateList(arr, head)
return head
def createLinkedListNode(next, value):
class LinkedListNode:
def __init__(self):
self.next = next
self.value = value
return LinkedListNode
deleteNode.py
from python.utils import createLinkedList, linkedListToArray
useCase1 = [4, 5, 1, 9]
linkedList = createLinkedList(useCase1)
^ linkedList.value doesn't exist?
Some misunderstandings with python classes:
The class LinkedListNode should not defined in function.
Return LinkedListNode is actually returning the class itself, but not the Instance. To return the instance, you have to call the class. return LinkedListNode()
Using next as instance variable is not ideal. next is an iteration function in python, so when you set self.next = next, you are actually assigning the function to self.next
If you want to set a variable, for example self.next_value = next_value, you should put next_value as a parameter of __init__ function, like def __init__(self, next_value)
Here is a simple demo of Linked List:
class LinkedList:
def __init__(self, value):
self.value = value
self.next_value = None
def __iter__(self):
yield self.value
if self.next_value is not None:
yield from self.next_value
# else raise StopIteration
def __getitem__(self, index):
if index == 0:
return self.value
else:
return self.next_value[index-1]
# recursively get the next value
def __str__(self):
return str(self.value) + ' -> ' + str(self.next_value)
def __len__(self):
if self.next_value is None:
return 1
else:
return 1 + len(self.next_value)
# recursively get the length
def append(self, value):
if self.next_value is None:
self.next_value = LinkedList(value, self)
else:
self.next_value.append(value)
a = LinkedList(2)
a.append(1)
a.append(3)
for num in a:
print(num, end=", ")
print()
print(a[1])
print(a)
print(len(a))
Output:
2, 1, 3,
1
2 -> 1 -> 3 -> None
3
createLinkedListNode() returns the LinkedListNode class itself, not an instance of the class.
Why are you defining classes and functions inside of other functions? That's an odd way of doing things.

Make Class Mutable by Subscription or Implement Views

Consider the following Python class definition:
class Custom:
def __init__(self, items):
self.items = items
def __getitem__(self, key):
return Custom(items[key])
As an example for what I want to achieve is the following code snippet to print "true" instead of "false":
custom = Custom([1, 2, 3, 4])
view = custom[0:2]
view.items[0] = 0
print(custom.items[0] == 0)
This is, I want to be able to subscript my class (which basically consists of lists only) in a way that makes __getitem__() return "views" of the instances in the sense that changes to the lists of the views propagate to the lists of the original instance.
I want my class to behave with its saved lists exactly like e.g. numpy arrays behave with the values it saves. The following prints "true":
array = np.array([1, 2, 3, 4])
view = array[0:2]
view[0] = 0
array == 0
How can I achieve this? Thanks!
Borrowing from this answer https://stackoverflow.com/a/3485490/10035985 :
import collections.abc
class ListSlice(collections.abc.Sequence):
def __init__(self, alist, start, alen):
self.alist = alist
self.start = start
self.alen = alen
def __len__(self):
return self.alen
def adj(self, i):
while i < 0:
i += self.alen
if i >= self.alen:
raise IndexError
return i + self.start
def __getitem__(self, i):
return self.alist[self.adj(i)]
def __setitem__(self, i, v):
self.alist[self.adj(i)] = v
def __delitem__(self, i, v):
del self.alist[self.adj(i)]
self.alen -= 1
def insert(self, i, v):
self.alist.insert(self.adj(i), v)
self.alen += 1
class Custom:
def __init__(self, items):
self.items = items
def __getitem__(self, key):
if isinstance(key, slice):
return ListSlice(self.items, key.start, key.stop - key.start)
return self.items[key]
custom = Custom([1, 2, 3, 4])
view = custom[1:3]
view[0] = 0
print(custom.items[1] == 0)
print(custom.items)
Prints:
True
[1, 0, 3, 4]
When we write
view[0] = 0
We're not calling __getitem__. We're calling __setitem__. That line is roughly equivalent to
view.__setitem__(0, 0)
So you need to implement that method
class Custom:
def __init__(self, items):
self.items = items
def __getitem__(self, key):
return Custom(self.items[key])
def __setitem__(self, key, value):
self.items[key] = value
# ... or whatever wrapping/unwrapping you want to do with the value

Implement a reversible iteration class to replace a generator

In python, I have a class with a method that returns a generator:
class foo():
data = [1, 2, 3]
def mygen(self):
for d in self.data:
yield d
instance = foo()
print([i for i in instance.mygen()])
But I can't reverse this:
print([i for i in reversed(instance.mygen())])
TypeError: 'generator' object is not reversible
So I thought I could implement a class which returns a generator when calling __iter__, like this
class foo():
data = [1, 2, 3]
def mygen(self):
return _ReversibleIterator(self)
class _ReversibleIterator(object):
def __init__(self, obj):
self.obj = obj
def __iter__(self):
for d in obj.data:
yield d
def __reversed__(self):
for d in reversed(obj.data):
yield d
But I think this isn't quite the same, because the _ReversibleIterator class doesn't have a next() method.
So what is the pythonic way to create a class method that returns an iterator that can be reversed()?
(Obviously I'm just using [1,2,3] as an example. The real thing to iterate over is less trivially reversible)
According to the docs, reversed must have one of two things to work with: a __reversed__ method OR a __len__ and a __getitem__ method. If you think about it, this makes sense because most generators can't support reversed because they generate results on the fly: they don't know what the next, let alone the last element is going to be. However, if you know its length and have random-access to any index, it can be reversed.
class foo():
data = [1, 2, 3]
def mygen(self):
return _ReversibleIterator(self)
class _ReversibleIterator(object):
def __init__(self, obj):
self.obj = obj
self.index = 0
def __iter__(self):
self.index = 0
return self
def __reversed__(self):
return reversed(self.obj.data)
def __next__(self):
try:
el = self.obj.data[self.index]
except IndexError:
raise StopIteration
self.index += 1
return el
or
class _ReversibleIterator(object):
def __init__(self, obj):
self.obj = obj
self.index = 0
def __iter__(self):
self.index = 0
return self
def __len__(self):
return len(self.obj.data)
def __getitem__(self, i):
return self.obj.data[i]
def __next__(self):
try:
el = self[self.index]
except IndexError:
raise StopIteration
self.index += 1
return el
By the way, if you would like, you can replace for d in whatever: yield d with yield from whatever.

What method do I need to override for list child to return internal data array instead of itself?

I needed a dynamic size queue so I inherited list and Iterable and I tested all the stuff like appending, indexing and iterating but there is a small problem. Methods like
str(DataSet) return an empty list, and the Python debugger does show an empty list, which is expected, since I don't append to the list parent but the internal data. What do I need to override to not return self but return self.data?
class DataSet(collections.Iterable, list):
data = collections.deque()
index = 0
max = 3
def __add__(self, other):
self.append(other)
def append(self, item):
if len(self.data) >= self.max:
self.data.popleft()
self.data.append(item)
def __next__(self):
if self.index < len(self.data):
ret = self.data[self.index]
self.index += 1
return ret
raise StopIteration
def __getitem__(self, i):
return self.data[i]
def __iter__(self):
self.index = 0
return self
def setMax(self, max):
self.max = max
def __len__(self):
return len(self.data)
Override
def __str__(self):
return str(self.data)

Checking if object inside heap

I am trying to check if an object is inside a heap. However, I keep getting this error:
AttributeError: 'tuple' object has no attribute 'X'
I have a Heap and a Cell class that look like this:
import heapq
class Heap(object):
def __init__(self):
self.heap = []
def push(self, priority, cell):
heapq.heappush(self.heap, (priority, cell))
def pop(self):
cell = heapq.heappop(self.heap)[1]
return cell
def contains(self, cell):
if(cell in self.heap):
return True
return False
def isEmpty(self):
return len(self.heap) == 0
Cell class:
class Cell(object):
def __init__(self, x, y):
self.X = x
self.Y = y
def __eq__(self, other):
return int(self.X) == int(other.X) and int(self.Y) == int(other.Y)
I use the Heap class like this: I get the error when I use the contains method.
from Heap import Heap
from Cell import Cell
class Test(object):
def __init__(self):
self.myHeap = Heap()
cell = Cell(2, 3)
self.myHeap.push(1, cell)
if self.myHeap.contains(cell) == False:
print("not in heap")
test = Test()
What am I doing wrong? Any help would be appreciated.
The problem is in containsmethod.
def contains(self, cell):
if(cell in self.heap):
return True
return False
self.head is a list of tuples of type (priority, Cell). And you actually compares Cells with elements of this list (tuples), so Cell.__eq__() method is called and it raises exception.

Categories

Resources