Списък, който расте
Проблем
Количката е пълна
Пишеш количката на онлайн магазин и пазиш продуктите в списък, както в урока за масивите: всеки продукт на своя индекс. След края обаче не може да се пише: cart[3] = "Масло" спира програмата с IndexError: list assignment index out of range. Затова за четвърти продукт правиш нов, по-дълъг списък. А когато клиентът махне продукт, остава дупка:
cart = ["Хляб", "Мляко", "Кафе"]
# Четвърти продукт: cart[3] = "Масло" спира с IndexError,# затова правим нов, по-дълъг списък.cart = cart + ["Масло"]
# Клиентът маха млякото.cart[1] = None
for item in cart: print(item)ХлябNoneКафеМаслоNone. А броят продукти вече не е len(cart).Това е много код за нещо, което всеки магазин прави постоянно.
Идеята
append и remove: списъкът се грижи сам
Списъкът има методи, които правят това вместо теб. Отвътре той пак пази елементите в масив, но копирането и местенето прави сам. Колекция, която расте и се свива така, се казва списък:
cart = []cart.append("Хляб")cart.append("Мляко")cart.append("Кафе")cart.append("Масло")cart.remove("Мляко")print(cart)print("Продукти:", len(cart))['Хляб', 'Кафе', 'Масло']Продукти: 3appendдобавя в края.removeмаха елемента с тази стойност и затваря дупката: „Кафе“ се мести на мястото на „Мляко“. Ако стойността е няколко пъти, маха първата.len(cart)е колко елемента има в момента.printпечата целия списък в квадратни скоби, а текста в кавички.
Ето какво прави списъкът вътре. Колко места да има масивът, не избираш ти, а CPython, програмата, която изпълнява кода ти. Списък от 4 продукта в кода има точно 4 места, затова масивът се пълни на петия:
cart = ["Хляб", "Мляко", "Кафе", "Масло"]cart.append("Сирене")cart.remove("Мляко")print(cart)print("Продукти:", len(cart))Стъпка по стъпка, с думи
- cart сочи обект list. Вътре има брояч ob_size и масив ob_item с 4 места.
- Продуктите от кода заемат местата един по един. Масивът е пълен: 4 от 4.
- Няма място. append заделя масив с 8 места и елементите отиват в него.
- ob_item вече сочи новия масив. „Сирене“ влиза на първото свободно място.
- remove маха „Мляко“, а всичко след него се мести с едно място наляво. Дупка не остава.
- Отвън се вижда само списък с четири продукта. Колко места има отвътре, решава CPython: езикът не го обещава.
Идеята
Какво има в списъка
Списъкът не пита какво слагаш в него. Текст и число могат да стоят едно до друго:
cart = []cart.append("Хляб")cart.append(5)print(cart)['Хляб', 5]Това изглежда удобно, но грешката излиза по-късно, там, където очакваш текст. Например цикъл, който печата продуктите с главни букви:
cart = ["Хляб", 5]for item in cart: print(item.upper())AttributeError: 'int' object has no attribute 'upper'„Хляб“ се отпечатва, а на числото програмата спира. Затова един списък пази един вид неща: само продукти, само цени.
Можеш да го запишеш с подсказка за типа (type hint): list[str] значи „списък от текст“.
cart: list[str] = []cart.append("Хляб")cart.append(5)print(cart)['Хляб', 5]Python не проверява подсказката, докато програмата върви: числото пак влиза. Проверяват я редакторът и инструменти като mypy, преди да пуснеш програмата. В този урок подсказки няма, но ще ги срещаш в чужд код.
Идеята
Индекси: [i], insert, index, in
Списъкът има индекси като масива, от 0 до len(tasks) - 1, и те се пишат със същите [ ]:
tasks = ["Фактура за март", "Обаждане до банката", "Отчет за склада"]
print(tasks[0])tasks[1] = "Среща в банката"tasks.insert(0, "Кафе с екипа")print(tasks)print(tasks.index("Отчет за склада"))print("Обаждане до банката" in tasks)Фактура за март['Кафе с екипа', 'Фактура за март', 'Среща в банката', 'Отчет за склада']3Falsetasks[i]чете,tasks[i] = xзаменя.insert(i, x)вмъква на индексiи мести останалите надясно.index(x)търси и връща индекса, аx in tasksказва само дали го има.
По-точно, за любопитните
- Индекс извън списъка спира програмата. Отрицателният индекс брои отзад:
tasks[-1]е последната задача. Аindexна нещо, което го няма, също спира програмата, затова първо питай сin:
tasks = ["Фактура за март", "Обаждане до банката", "Отчет за склада"]try: print(tasks[3])except IndexError as e: print(f"IndexError: {e}")print(tasks[-1])try: tasks.index("Заплати")except ValueError as e: print(f"ValueError: {e}")IndexError: list index out of rangeОтчет за складаValueError: 'Заплати' is not in listinиindexсравняват с==. Затова намират текст, който е равен, а не само същия обект.- Вмъкването и махането отпред са бавни при голям списък: всички елементи след индекса се местят с едно място. За един милион елемента това е един милион премествания.
Пробвай
Кой е на ред?
Списъкът с чакащите при лекаря. Иван влиза, а Ана има час и се нарежда втора.
Какво ще отпечата програмата?
waiting = ["Иван", "Мария", "Петър"]waiting.pop(0)waiting.insert(1, "Ана")print(waiting, len(waiting))pop(0) маха Иван и Мария става първа, на индекс 0. insert(1, "Ана") вмъква Ана на индекс 1 и бута Петър надясно. Остават трима.
Идеята
Обхождане
Списъкът се обхожда с for, когато ти трябват само елементите. Когато ти трябва и номерът, има enumerate:
cart = ["Хляб", "Кафе", "Масло"]
for item in cart: print(f"- {item}")for number, item in enumerate(cart, start=1): print(f"{number}. {item}")- Хляб- Кафе- Масло1. Хляб2. Кафе3. Маслоenumerate(cart, start=1) дава на всяка стъпка две стойности: номера и елемента. Без start=1 номерата започват от 0, като индексите. Може и с индекс, както в урока за масивите, for i in range(len(cart)), но enumerate е обичайният начин в Python.
По-точно, за любопитните
- Не махай елементи от списъка, докато го обхождаш с
for. Програмата не спира, но прескача елементи:
cart = ["Хляб", "Хляб", "Мляко"]for item in cart: if item == "Хляб": cart.remove(item)print(cart)['Хляб', 'Мляко']Вторият „Хляб“ остана. remove премести всичко наляво, а цикълът продължи от следващия индекс и не го видя. Грешка няма, затова такъв пропуск се хваща трудно.
- Ако трябва да махаш, обхождай с индекс отзад напред. Така махането не мести елементите, които още не си проверил:
cart = ["Хляб", "Хляб", "Мляко"]for i in range(len(cart) - 1, -1, -1): if cart[i] == "Хляб": cart.pop(i)print(cart)['Мляко']Идеята
Числа в списък
Списък от оценки е като списък от текст. Числото стои в списъка, както си е:
scores = [85, 64, 91]
total = 0for score in scores: total += scoreprint("Средно:", total / len(scores))print("Средно:", sum(scores) / len(scores))Средно: 80.0Средно: 80.0/дава дробно число, затова средното е80.0, а не 80.sumсъбира всички числа в списъка. Двата реда смятат едно и също.
По-точно, за любопитните
- В Python и числото е обект. За малките числа, от -5 до 256, Python пази готови обекти, а за по-големите прави нови. Затова
is, който пита „същият обект ли е?“, дава различни отговори:
a = int("256")b = int("256")c = int("257")d = int("257")print(a is b)print(c is d)print(c == d)TrueFalseTrueЧислата и текста сравнявай с ==: той пита дали стойностите са равни. is е за None: if found is None.
Пробвай
Намери грешката
Залата пази заетите места в списък. Клиентът се отказва от място 2 и програмата трябва да отпечата [12, 7, 5]. Вместо това печата [12, 2, 5]. На кой ред е грешката?
Избери реда с грешката.
pop(i) маха по индекс: pop(2) маха елемента на индекс 2, тоест мястото 7. По стойност маха remove: seats.remove(2). При списък от текст двата метода не се бъркат лесно, а при числа и индексът, и стойността са числа.Идеята
Подреждане и списък, който не се променя
sort() подрежда списъка на място: текста по азбучен ред, числата по големина. sorted(...) не пипа нищо, а връща нов, подреден списък.
cities = ["София", "Пловдив", "Варна"]cities.sort()print(cities)
prices = (30, 5, 12)print(sorted(prices))['Варна', 'Пловдив', 'София'][5, 12, 30]Цените са в кръгли скоби: (30, 5, 12). Това е кортеж (tuple), списък, който е неизменим. Можеш да го четеш и обхождаш, но не можеш да добавяш, махаш или подреждаш в него:
days = ("пн", "вт", "ср")days.append("чт")AttributeError: 'tuple' object has no attribute 'append'Затова по-горе цените са подредени със sorted: той връща нов списък, а кортежът остава същият. Кортежът е за неща, които не се менят: дните от седмицата, двете координати на точка.
По-точно, за любопитните
- Текстът се подрежда по кодовете на символите. Главните букви са преди малките, затова „Ябълка“ е преди „ананас“:
fruit = ["ананас", "Ябълка", "банан", "Круша"]fruit.sort()print(fruit)['Круша', 'Ябълка', 'ананас', 'банан']- Как се подреждат твои обекти, например продукти по цена, ще видиш в урока „Класове в класове“.
Упражнение
Списъци в класове
_ отпред („не го пипай отвън“), а кодът под класовете вика само методите му.1. Рафт с книги. Клас BookShelf с методи add_book(title), който слага книгата в края, и take_book(), който връща и маха първата книга (или None, ако рафтът е празен). Добави count() и __str__(). Сложи „Под игото“, „Тютюн“ и „Железният светилник“, вземи една книга и отпечатай останалите.
Взе: Под иготоОстават 2: ['Тютюн', 'Железният светилник']2. Журнал на събитията. Клас Event с дата и описание и клас EventLog с методи add_event(date, description) и get_events_between(start, end), който връща списък със събитията между двете дати, включително. Датите са текст във вида 2026-03-31. В този вид текстът се подрежда като датите, затова <= и >= ги сравняват вярно. Добави четири събития: 2026-01-15 „Годишна инвентаризация“, 2026-03-31 „Отчет за първото тримесечие“, 2026-05-20 „Нов доставчик на кафе“, 2026-09-01 „Нов учебен сезон“. Отпечатай тези от 2026-03-01 до 2026-06-30.
2026-03-31 Отчет за първото тримесечие2026-05-20 Нов доставчик на кафе3. Тетрадка. Клас Note със заглавие и съдържание и клас Notebook с методи add_note, find_note(title) (връща бележката или None), delete_note(title) (връща True, ако е изтрил) и count(). Добави бележки „ООП“, „Колекции“ и „SQL“, намери „Колекции“, изтрий „ООП“ и провери, че вече я няма.
Колекции: списъкът расте самИзтрита: ООПБележки: 2Няма бележка ООП.4. Смятане наум. Клас Question с текст и верен отговор и клас MathGame с списък от въпроси, метод add_question и метод start(). start задава въпросите един по един, чете отговора и накрая казва колко са верните. Въпросите са 2 + 2, 3 * 4 и 15 - 9.
41272 + 2 = Вярно!3 * 4 = Вярно!15 - 9 = Грешно, отговорът е 6.Резултат: 2 от 3Още задачи
- Без повторения. Прочети 6 имена и ги събери в списък, като пропускаш тези, които вече са в него. При вход Иван, Мария, Иван, Ана, Мария, Петър изходът е
['Иван', 'Мария', 'Ана', 'Петър']. - Най-скъпият продукт. В списък има цени. Намери най-високата и на кой индекс е, без
max. - Обърни списъка. Напиши функция, която връща нов списък със същите елементи в обратен ред. Не променяй първоначалния.
- Списък за пазаруване. Чети команди
add мляко,remove мляко,listиendи пази списъка между тях. Командата за триене на продукт, който го няма, казва „Няма такъв продукт.“
Тест