Obiekt mapy Python nie jest indeksowany

83

Dlaczego następujący skrypt wyświetla błąd:

payIntList[i] = payIntList[i] + 1000
TypeError: 'map' object is not subscriptable

payList = []
numElements = 0

while True:
        payValue = raw_input("Enter the pay amount: ")
        numElements = numElements + 1
        payList.append(payValue)
        choice = raw_input("Do you wish to continue(y/n)?")
        if choice == 'n' or choice == 'N':
                         break

payIntList = map(int,payList)

for i in range(numElements):
         payIntList[i] = payIntList[i] + 1000
         print payIntList[i]
stacja
źródło
5
Czy używasz Pythona 3?
Felix Kling
@ user567797 - To działa dobrze dla mnie @ Felix: Nie używa Pythona 3, ponieważ używa print jako instrukcji!
Pushpak Dagade
2
Całość poniżej pętli while można skrócić do payIntList = [int(x) + 1000 for x in payList]; print(*payIntList, sep='\n')(lub for x in payIntList: print xw Pythonie 2.x, gdzie printnie jest funkcją) bez utraty czytelności (prawdopodobnie jest jeszcze bardziej czytelna).
2
@Guanidene: Używa Pythona 3, ponieważ ma obiekty mapy. Ale on próbuje uruchomić na nim kod Pythona 2, stąd błędy.
Lennart Regebro
3
Chciałbym zadać pytanie „Dlaczego dostaję błędy, kiedy uruchamiam kod Python 2 na Pythonie 3”, do którego możemy oznaczyć je wszystkie jako duplikaty. ;)
Lennart Regebro

Odpowiedzi:

138

W Pythonie 3 mapzwraca iterowalny obiekt typu map, a nie listę z indeksem, co pozwoliłoby na pisanie map[i]. Aby wymusić wynik listy, napisz

payIntList = list(map(int,payList))

Jednak w wielu przypadkach możesz lepiej napisać kod, nie używając indeksów. Na przykład ze składanymi listami :

payIntList = [pi + 1000 for pi in payList]
for pi in payIntList:
    print(pi)
phihag
źródło
6
Ta odpowiedź jest poprawna, ale idealnie by było, gdybyś nie konwertował map na listy do takich przypadków użycia. Mapy są już iterowalne i wydają się być o rząd wielkości szybsze do iteracji w porównaniu z listami. Więc nie tylko nie potrzebujesz indeksu, ale w ogóle nie potrzebujesz listy. Czystszy kod byłby taki, ponieważ payIntList jest już mapą,for i in payIntList: print(i + 1000)
RustyToms
17

map()nie zwraca listy, zwraca mapobiekt.

Musisz zadzwonić, list(map)jeśli chcesz, aby była to ponownie lista.

Nawet lepiej,

from itertools import imap
payIntList = list(imap(int, payList))

Nie zajmie dużo pamięci, tworząc obiekt pośredni, po prostu zemdleje podczas intsich tworzenia.

Możesz też to zrobić, if choice.lower() == 'n':aby nie robić tego dwa razy.

Python obsługuje +=: możesz to zrobić payIntList[i] += 1000i numElements += 1jeśli chcesz.

Jeśli naprawdę chcesz być podstępny:

from itertools import count
for numElements in count(1):
    payList.append(raw_input("Enter the pay amount: "))
    if raw_input("Do you wish to continue(y/n)?").lower() == 'n':
         break

i / lub

for payInt in payIntList:
    payInt += 1000
    print payInt

Ponadto cztery spacje to standardowa wielkość wcięcia w Pythonie.

agf
źródło
1
Tak jest w przypadku python3, ale wydaje się, że używa on python2.x, ponieważ używa print jako instrukcji.
Pushpak Dagade
Twój fragment kodu ma przechowywać wszystkie ints w pamięci w postaci listy. Używanie iteratorów jest rzeczywiście przydatne i oszczędza pamięć, szczególnie w przypadku wielu warstw transformacji, ale dodanie listwokół iteratora odbiera tę korzyść!
1
Kiedy to zrobisz list(map(...)), tworzy a map, tworzy a list, a następnie usuwa map, więc na chwilę oba są w pamięci jednocześnie. Kiedy to robisz, list(imap(...))tak nie jest. Dlatego powiedziałem „weź pamięć za pomocą obiektu pośredniego
agf
Więc zakładasz Python 2? Wtedy list(map(...))jest zbędny, ponieważ - jak stwierdza dokumentacja , „wynikiem mapjest zawsze lista”.