"object is not iterable" error on my python implementation of iterable - python

I am trying to create a iterable class but have been banging my head on a wall so to speak, getting "object is not iterable". This is my code:
class myiterable:
def __init__(self, somelist):
self.i = 0
self.l = somelist
def __iter__(self):
return self
def __next__(self):
if self.i < len(self.l):
self.i = self.i + 1
return self.l[self.i-1]
else:
raise StopIteration
for i in myiterable([1, 2, 3, 4, 5]):
print(i)
What's wrong? I have also tried next(self) in lieu of __next__(self) to no avail!

There are several problems with your code:
indentation
if you are on python 2, you should have defined next() method instead of __next__() (leave it as is if on python 3)
++self.i should be replaced with self.i += 1
self.l[i-1] should be replaced with self.l[self.i-1]
class myiterable:
def __init__(self, somelist):
self.i = 0
self.l = somelist
def __iter__(self):
return self
def next(self):
if self.i < len(self.l):
self.i += 1
return self.l[self.i-1]
else:
raise StopIteration
for i in myiterable([1, 2, 3, 4, 5]):
print(i)
prints:
1
2
3
4
5

If you just copied your code, then it should be because of bad indent. Pull __iter__ and __next__ to same indent as __init__.

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.

Class iteration over internal numpy array ? Assignment?

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]

The value none is being returned Fibonacci sequence

I'm completely confused as to why my code is returning none. I'm new and have been searching for a while and i'm still lost.
class Fibonacci:
def __init__(self, max = 0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max: # if n is less that 0
def FIB(number):
if number == 1 or number == 2:
return 1
else:
return FIB(number - 1) + FIB(number - 2)
self.n += 1
else:
raise StopIteration
Your implementation spends more and more time every time you call __next__. should have used an iterative method instead with the constant complexity of iteration over it:
class Fibonacci:
def __init__(self, max = 0):
self.max = max
self.a = 0
self.b = 1
def __iter__(self):
self.a = 0
self.b = 1
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a <= self.max: # if n is less that 0
return self.a
else:
raise StopIteration

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.

Purpose of return self python

I have a problem with return self
class Fib:
def __init__(self, max):
self.max = max
def __iter__(self):
self.a = 0
self.b = 1
return self
def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib
I have already seen this question return self problem but I can't understand what the benefit is of return self?
Returning self from a method simply means that your method returns a reference to the instance object on which it was called. This can sometimes be seen in use with object oriented APIs that are designed as a fluent interface that encourages method cascading. So, for example,
>>> class Counter(object):
... def __init__(self, start=1):
... self.val = start
... def increment(self):
... self.val += 1
... return self
... def decrement(self):
... self.val -= 1
... return self
...
>>> c = Counter()
Now we can use method cascading:
>>> c.increment().increment().decrement()
<__main__.Counter object at 0x1020c1390>
Notice, the last call to decrement() returned <__main__.Counter object at 0x1020c1390>, which is self.
Now:
>>> c.val
2
>>>
Notice, you cannot do this if you did not return self:
>>> class Counter(object):
... def __init__(self, start=1):
... self.val = start
... def increment(self):
... self.val += 1
... # implicitely return `None`
... def decrement(self):
... self.val -= 1
... # implicitely return `None`
...
>>> c = Counter()
>>> c.increment().increment()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'increment'
>>> c
<__main__.Counter object at 0x1020c15f8>
>>> c.val
2
>>>
Notice, not everyone is a fan of "method cascading" design. Python built-ins do not tend do this, so, list for example:
>>> x = list()
>>> x.append(1).append(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'append'
>>>
The one place you do often see this is when your class implements the iterator protocol, where iter on an iterator returns self by convention, although this is suggested by the docs:
Having seen the mechanics behind the iterator protocol, it is easy to
add iterator behavior to your classes. Define an __iter__() method
which returns an object with a __next__() method. If the class
defines __next__(), then __iter__() can just return self:
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
Notice, this in effect makes your iterator only useful for a single pass (as it should be to properly follow the iterator protocol):
>>> x = [1, 2, 3, 4]
>>> it = iter(x)
>>> list(it)
[1, 2, 3, 4]
>>> list(it)
[]
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
This is needlessly complex code. Pay little attention to it. There's no reason on earth to implement it this way.
That being said, what it does is this:
class Fib:
"""Implements the Fibonacci sequence."""
def __init__(self, max_):
self.max = max_
def __iter__(self):
"""Initializes and returns itself as an iterable."""
self.a = 0
self.b = 1
return self
def __next__(self):
"""What gets run on each execution of that iterable."""
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b # increment
return fib
This is all much easier to express as:
def fib(max_):
a, b = 0, 1
while b <= max_:
out = a
a, b = b, a+b
yield out
Examples:
>>> fib_obj = Fib(20)
>>> for n in fib_obj:
... print(n)
>>> for n in Fib(20):
... print(n)
>>> for n in fib(20):
... print(n)
# all give....
0
1
1
2
3
5
8
13

Categories

Resources