ArekBulski
ArekBulski

Reputation: 5118

TypeError: argument to reversed() must be a sequence

How come enumerate does not produce a sequence?

----> 1 BytesInt('1B')

     12 def BytesInt(s):
     13     suffixes = ['B','KB','MB','GB','TB','PB','EB','ZB','YB']
---> 14     for power,suffix in reversed(enumerate(suffixes)):
     15         if s.endswith(suffix):
     16             return int(s.rstrip(suffix))*1024**power

TypeError: argument to reversed() must be a sequence

Upvotes: 4

Views: 9942

Answers (4)

ArekBulski
ArekBulski

Reputation: 5118

Apparently it is possible to implement enumerate so it produces a sequence when given a sequence, and produces an iterable otherwise. Sequence can be safely reversed. This works by replacing the builtin function. Other similar functions could be made this way, too.

class EnumeratedSequence:
    def __init__(self, items):
        self.items = items

    def __getitem__(self, index):
        return (index,self.items[index])

    def __len__(self):
        return len(self.items)

def enumerate(items):
    if hasattr(items, '__getitem__'):
        print 'Sequence detected'
        return EnumeratedSequence(items)
    else:
        print 'Iterator detected'
        return __builtin__.enumerate(items)

print list(reversed(enumerate('abcdef')))
print list(enumerate(reversed('abcdef')))

Sequence detected
[(5, 'f'), (4, 'e'), (3, 'd'), (2, 'c'), (1, 'b'), (0, 'a')]
Iterator detected
[(0, 'f'), (1, 'e'), (2, 'd'), (3, 'c'), (4, 'b'), (5, 'a')]

Upvotes: 0

PM 2Ring
PM 2Ring

Reputation: 55489

enumerate doesn't produce a sequence because it's useful to be able to enumerate any iterable, not just sequences. Eg, you can enumerate over an infinite generator.

from random import randint

def randgen(lo, hi):
    while True:
        yield randint(lo, hi)

for i, v in enumerate(randgen(1, 6)):
    print(i, v)
    if i == 20:
        break

Martijn has shown a nice way to do what you want by calling reversed on the sequence. And as Daniel points out, for such a small sequence converting the iterable produced by enumerate to a list is quite acceptable, as well as resulting in compact code.

Another option would be to zip a range with your reversed sequence, but I think Daniel's and Martijn's ways are better here.

def BytesInt(s):
    suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
    for power, suffix in zip(range(len(suffixes) - 1, -1, -1), reversed(suffixes)):
        if s.endswith(suffix):
            return int(s.rstrip(suffix)) * 1024 ** power

for s in ('1B', '1KB', '1TB'):
    print(BytesInt(s))

output

1
1024
1099511627776

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1123850

enumerate() produces an iterator, not a sequence. A sequence is addressable (can be subscribed with any index), while an iterator is not.

Either don't use enumerate(), subtract from len(suffixes) or convert the enumerate() output to a list.

Subtraction gives you the advantage of avoiding materialising a list:

for index, suffix in enumerate(reversed(suffixes), 1):
    power = len(suffixes) - index

Demo:

>>> def BytesInt(s):
...     suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
...     for index, suffix in enumerate(reversed(suffixes), 1):
...         power = len(suffixes) - index
...         if s.endswith(suffix):
...             return int(s.rstrip(suffix)) * 1024 ** power
...
>>> BytesInt('1B')
1
>>> BytesInt('1KB')
1024
>>> BytesInt('1TB')
1099511627776

Upvotes: 4

Daniel Roseman
Daniel Roseman

Reputation: 599856

enumerate indeed does not return a sequence, it is a generator. If your input is relatively small you can convert it to a list:

for power, suffix in reversed(list(enumerate(suffixes))):

Upvotes: 6

Related Questions