Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attribute Error: next()

Tags:

python

I want to loop one level deep using next() with os.walk

Critical line of my code:

for root, dirs, files in os.walk(dir).next(1): 

Error:

AttributeError: 'generator' object has no attribute 'next'

I tried using .next(x) to replace old next()[1] as suggested by community, but this also doesnt work.

like image 690
Biker John Avatar asked Aug 31 '13 11:08

Biker John


2 Answers

You are using python3. In python3 the next() method was replaced by __next__. These method do not accept any argument(i.e. a.__next__(1) is an error). They advance the iterator by one. To advance it by more elements call next repeatedly.

If you want to advance the iterator by one use, I'd suggest to use the next built-in function:

>>> L = (x for x in range(10))
>>> next(L)
0
>>> next(L)
1

Note: the next built-in function was added in python2.6, I believe, so it's safe to use even in python2.

However, in your code it does not make sense calling next at all. What are you trying to achieve with it?

Doing:

for root, dirs, files in next(os.walk(dir)):

Will raise an error, since next returns the first element of os.walk, which is a three element tuple, containings lists of strings. But the for loop will iterate over the tuple, trying to unpack a single list into root, dirs, files. If any directory has more or less than 3 files/subdirectories the code will fail.

If you want to skip only the first directory you'll have to call next separately:

iterable = os.walk(directory)
next(iterable)   # throw away first iteration
for root, dirs, files in iterable:
   #...

If you wanted to iterate only on the directories, as speculated by Martijn, then you don't have to do anything in particular. Simply don't use the root and files variables in the loop. In this case I'd suggest to rename them to _, which is often used to indicate a variable that we must assign, but isn't used at all:

for _, dirs, _ in os.walk(directory):
    # Work only on "dirs". Don't care for "_"s

If you want to consume the first n elements of an iterable, you can use itertools.islice and collections.deque to do it fast and without memory consumption:

from itertools import islice
from collections import deque

def drop_n_elements(n, iterable):
    deque(islice(iterable, n), maxlen=0)

And then use it as:

iterable = os.walk(directory)
drop_n_elements(N, iterable) # throw away first N iterations
for root, dirs, files in iterable:
    # ...

It just occurred to me that there is an even faster and easier way to drop the first n elements of an iterable:

def drop_n_elements(n, iterable):
    next(islice(iterable, n, n), None)

It is slightly faster than using deque(..., maxlen=0) because it does only a single call to the next method of islice.

like image 174
Bakuriu Avatar answered Oct 02 '22 11:10

Bakuriu


for root, dirs, files in os.walk(dir).__next__()[1]:
like image 25
sankeeta kamath Avatar answered Oct 02 '22 10:10

sankeeta kamath