Here I use a simplified code to demonstrate what I've encountered:
def recTry(inList):
print(inList)
if len(inList) < 10:
recTry(inList.append(1))
I hope with recursion it'll grow like [1], [1, 1], [1, 1, 1] ...
But actually run that will yield this:
>>> recTry([1])
[1]
None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in recTry
File "<stdin>", line 3, in recTry
TypeError: object of type 'NoneType' has no len()
inList become None since the second recursion.
So How can I code to make the inList appended with each recursion?
inList.append(1) returns None so you pass None while recursion
what you can do is:
def recTry(inList):
print(inList)
if len(inList) < 10:
inList.append(1)
recTry(inList)
else:
return inList
you try to call function with result of inList.append(1) but it is None
try to:
if len(inList) < 10:
inList.append(1)
recTry(inList)
It is a convention in Python that methods that mutate sequences return None. Thus the value your passing in first returns nothing which is your problem.
On the 4th line of your function,
recTry(inList.append(1))
inList.append(1) returns None after appending 1 to the existing list. This None is what you are passing to recTry method. You can try this instead to make the inList appended with each recursion:
inList.append(1)
recTry(inList)
Related
I wrote a simple code to check if a list is sorted or not.
I have two questions:
First, my result is wrong. I guess the issue is with following the line. Which one is correct?:
sorted_list = mylist[:].sort()
sorted_list = list(mylist[:]).sort()
As second question, when I try to print(sorted_list), I get NameError which is sorted_list is not defined which is weird. Because I've already defined it in the second line. Would you please help me understand why I'm getting this error?
def is_sorted(mylist):
sorted_list = mylist[:].sort()
# mylist.sort()
if sorted_list == mylist:
return True
else:
return False
print(is_sorted(['Aajid', 'Bafiee', 'Hello']))
print(sorted_list)
Output:
False
Traceback (most recent call last):
File "e:\NectCloud\Python\is_sorted.py", line 11, in <module>
print(sorted_list)
^^^^^^^^^^^
NameError: name 'sorted_list' is not defined
Thanks
Use
mylist = [1, 5, 3, 6, 2]
sorted_list = sorted(mylist)
mylist.sort() does inplace sorting and returns None. For more information about sorting see https://docs.python.org/3/howto/sorting.html.
sorted_list is not defined in outer scope since it is only defined in the function scope. See https://realpython.com/python-scope-legb-rule/.
This question already has answers here:
Why can't I iterate twice over the same iterator? How can I "reset" the iterator or reuse the data?
(5 answers)
Closed 1 year ago.
I am confused about iterator in python. Please take a look on the code.
class MyNumbers:
def __init__(self):
self.a=4
def __iter__(self):
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter1 = iter(myclass)
c=list(myiter1)
for x in myiter1:
print(x)
I getting no output from the above code. I was expecting some value iteration from 4.
but when i was removing c=list(myiter1) i was getting expected output. So i am confused about it. why is it happening.
Your iterator is exhausted: list in the assignment to c consumed all the available values, resulting in a list [4, 5, 6, ..., 20] and an iterator that will raise StopIteration immediately when you try to iterate over it again.
You may be confused because it appears you can iterate over a list multiple times. But that is because a list itself is not an iterator.
>>> list.__next__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'list' has no attribute '__next__'
Calling iter on a list produces a list_iterator value, not the list itself. That iterator is exhaustible like any other iterator.
>>> x = [1,2,3]
>>> y = iter(x)
>>> type(y)
<class 'list_iterator'>
>>> list(y)
[1, 2, 3]
>>> list(y)
[]
The for loop implicitly calls iter on the thing you are try to iterate over. By convention, all iterators are also iterable: an iterator's __iter__ should return self.
In discussion of this answer we realized that tuples do not have a __reversed__ method. My guess was that creating the iterator would require mutating the tuple. And yet tuples play fine with reversed. Why can't the approach used for reversed be made to work for __reversed__ as well?
>>> foo = range(3)
>>> foo
[0, 1, 2]
>>> list(foo.__reversed__())
[2, 1, 0]
>>> foo
[0, 1, 2]
>>> bar = (0, 1, 2)
>>> list(bar.__reversed__())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute '__reversed__'
>>> reversed(bar)
<reversed object at 0x10603b310>
>>> tuple(reversed(bar))
(2, 1, 0)
According to the spec:
reversed(seq)
Return a reverse iterator. seq must be an object which has a
reversed() method or supports the sequence protocol (the __len__() method and the __getitem__() method with integer arguments starting at
0).
EDIT: well, I try to say it again (english is not my native language) - with example.
Some functions - I call them fun(object) - can use object.__func__() to do the job or they use other functions in object if there is no object.__func__()
-
For example str() - when you use str(object) it calls object.__str__(), but if there is no object.__str__() then it calls object.__repr__().
So you can use str() with object which has no __str__() and still get some result.
-
Other example < - when you use a < b it tries to use a.__lt__() but if there is no a.__lt__() it tries to use a.__gt__() (and maybe other functions too)
class MyClass():
def __str__(self):
return "MyClass: __str__"
def __repr__(self):
return "MyClass: __repl__"
#-------------------------
def __lt__(self, other):
return True
def __gt__(self, other):
return True
#-------------------------
a = MyClass()
b = MyClass()
print( str(a) )
print( a < b )
You can remove __str__ to get different result.
You can change True/False in __lt__ and you can see that result is changed.
Then you can remove __lt__ and you can change True/False in __gt__ and you see that result is changed again.
So, my overall goal with this was to compare CSV files, but then I ran into this problem.
Here's where the issue is:
import csv
csv1 = "C:\\somefile.csv"
file1 = csv.reader(open(csv1))
print len(list(file1))
print file1.next()
and for whatever reason, I get a stop iteration error.
Is the len(list(file1)) altering file1 for some reason that I'm just not aware of?
If I put a "print file1" before and after the len(list(file1)), they are both csv reader objects, so it doesn't make much sense to me that .next() wouldn't work.
Yes, it's the len(list(file1)). Observe:
>>> myiter = (i for i in range(3))
>>> len(list(myiter))
3
>>> myiter.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
And:
>>> myiter = (i for i in range(3))
>>> myiter.next()
0
>>> myiter.next()
1
>>> myiter.next()
2
>>> myiter.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
It's not len() which is causing the generator to reach the end, but it's the list() which turns the generator into a list by taking an item one by one from the iterator, resulting in the generator being exhausted (i.e finished).
To avoid this, simply make a list out of the generator:
>>> myiter = (i for i in range(3))
>>> temp = list(myiter)
>>> len(temp)
3
>>> temp
[0, 1, 2]
If you ever need it as an iterator again, you can always call iter()
When I execute this code in python 2.6
reduce(lambda x,y: x+[y], [1,2,3],[])
I get [1, 2, 3] as expected.
But when I execute this one (I think it is equivalent to previous)
reduce(lambda x,y: x.append(y), [1,2,3],[])
I get an error message
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'append'
Why these two lines of code do not give the same result?
x.append(y) is not equivalent to x+[y]; append modifies a list in place and returns nothing, while x+[y] is an expression that returns the result.
reduce calls the function and uses the return value as the new result. append returns None, and therefore the next append invocation fails. You could write
def tmpf(x,y):
x.append(y)
return x
reduce(tmpf, [1,2,3], [])
and get the correct result. However, if the result is a list of the same size as the input, you're not looking for reduce: The result of reduce should usually be a single value. Instead, use map or simply
[x for x in [1,2,3]]
The function argument to reduce is expected to return the result of the operation.
x+[y] does that, whereas x.append(y) doesn't (the latter modifies x and returns None).
Just to explain the error message:
AttributeError: 'NoneType' object has no attribute 'append'
The expression
reduce(lambda x,y: x.append(y), [1,2,3],[])
is equivalent to
[].append(1).append(2).append(3)
Since [].append(1) does not return a value, i.e. it returns None it tries to execute (in the second step)
None.append(2)
which results in the error message Nonetype object has no attribute append