""" Урок 28. Списки. Работа с памятью — весь код примеров одним файлом. Источник: subjects/python-fundamentals/course/lessons/28-lists-memory/examples.html Файл собран автоматически (tools/build_lesson_examples.py): правьте страницу урока. Запуск: python lesson-28.py """ # ==================================================================== # Пример 1. Переменная — это ссылка на объект # ==================================================================== numbers = [1, 2, 3] print(numbers) # [1, 2, 3] print(id(numbers)) # 2691946361536 (у вас будет другое число) print(type(numbers)) # # ==================================================================== # Пример 2. Присваивание списка — общая ссылка, а не копия # ==================================================================== items = ["Bread", "Milk"] same_items = items same_items.append("Eggs") print(items) # ['Bread', 'Milk', 'Eggs'] print(same_items) # ['Bread', 'Milk', 'Eggs'] print(id(items) == id(same_items)) # True # ==================================================================== # Пример 3. is против ==: тождество и равенство # ==================================================================== a = [1, 2, 3] b = [1, 2, 3] c = a print(a == b) # True — содержимое одинаковое print(a is b) # False — это два разных объекта print(a == c) # True print(a is c) # True — c и a ссылаются на один и тот же объект # ==================================================================== # Пример 4. Неизменяемые объекты ведут себя иначе # ==================================================================== text1 = "hello" text2 = text1 print(id(text1) == id(text2)) # True — пока объект один text2 += " world" print(text1) # hello print(text2) # hello world print(id(text1) == id(text2)) # False — text2 указывает на новый объект # ==================================================================== # Пример 5. Вложенные списки: несколько уровней индексации # ==================================================================== matrix = [[1, 2], [3, 4], [5, 6]] print(matrix[0][1]) # 2 for row_index, row in enumerate(matrix): for col_index, value in enumerate(row): matrix[row_index][col_index] = value + 1 print(matrix) # [[2, 3], [4, 5], [6, 7]] deep_list = [[1, 2, 3], [4, 5], 6, [7, [8, [9], 10]]] print(deep_list[3][1][2]) # 10 print(deep_list[3][1][1][0]) # 9 # ==================================================================== # Пример 6. del: удаляет имя, элемент или срез — но не возвращает значение # ==================================================================== numbers = [10, 20, 30, 40, 50] del numbers[2] # удаляем элемент с индексом 2 (30) print(numbers) # [10, 20, 40, 50] del numbers[1:3] # удаляем срез — элементы 20 и 40 print(numbers) # [10, 50] old_numbers = [1, 2, 3] new_numbers = old_numbers del old_numbers # удалили только имя old_numbers print(new_numbers) # [1, 2, 3] — объект жив, на него всё ещё ссылается new_numbers try: print(old_numbers) except NameError as error: print("NameError:", error) # NameError: name 'old_numbers' is not defined # ==================================================================== # Пример 7. Поверхностная копия: list(), .copy(), срез [:] # ==================================================================== original = [[1, 2], [3, 4]] copy_slice = original[:] copy_method = original.copy() copy_list_call = list(original) original.append([5, 6]) print(copy_slice) # [[1, 2], [3, 4]] — новую строку копии не увидели print(copy_method) # [[1, 2], [3, 4]] print(copy_list_call) # [[1, 2], [3, 4]] original[0][0] = 0 print(original) # [[0, 2], [3, 4], [5, 6]] print(copy_slice) # [[0, 2], [3, 4]] — вложенный список общий с оригиналом! print(copy_method) # [[0, 2], [3, 4]] print(copy_list_call) # [[0, 2], [3, 4]] # ==================================================================== # Пример 8. Глубокая копия: copy.deepcopy() # ==================================================================== import copy nested_list = [[10, 15, 20], [5, 25, 30], [35, 40, 80]] filtered = copy.deepcopy(nested_list) for row in filtered: average = sum(row) / len(row) row[:] = [value for value in row if value >= average] print("Исходный список:", nested_list) # Исходный список: [[10, 15, 20], [5, 25, 30], [35, 40, 80]] print("Отфильтрованная копия:", filtered) # Отфильтрованная копия: [[15, 20], [25, 30], [80]] # ==================================================================== # Пример 9. Ловушка [[0] * n] * n: общая ссылка на подсписки # ==================================================================== board = [[0] * 3] * 3 print(board) # [[0, 0, 0], [0, 0, 0], [0, 0, 0]] board[0][0] = 1 print(board) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] — изменилась КАЖДАЯ строка board_fixed = [[0] * 3 for _ in range(3)] board_fixed[0][0] = 1 print(board_fixed) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]] — теперь строки разные объекты # ==================================================================== # Пример 10. Список как аргумент функции: мутация видна снаружи # ==================================================================== def add_bonus_item(cart): cart.append("gift") return cart my_cart = ["book", "pen"] result = add_bonus_item(my_cart) print(my_cart) # ['book', 'pen', 'gift'] print(result) # ['book', 'pen', 'gift'] print(id(my_cart) == id(result)) # True — тот же объект def replace_cart(cart): cart = ["new", "cart"] # это создаёт НОВОЕ локальное имя cart return cart cart2 = ["book"] result2 = replace_cart(cart2) print(cart2) # ['book'] — исходный объект не тронут print(result2) # ['new', 'cart'] # ==================================================================== # Пример 11. Ловушка изменяемого аргумента по умолчанию # ==================================================================== def add_item_buggy(item, basket=[]): basket.append(item) return basket print(add_item_buggy("apple")) # ['apple'] print(add_item_buggy("banana")) # ['apple', 'banana'] — старая корзина никуда не делась! def add_item_fixed(item, basket=None): if basket is None: basket = [] basket.append(item) return basket print(add_item_fixed("apple")) # ['apple'] print(add_item_fixed("banana")) # ['banana'] — каждый вызов начинает с чистого списка # ==================================================================== # Пример 12. Изменение списка во время обхода: границы применимости # ==================================================================== nums_forward = [2, 5, 1, 8, 3] try: for index in range(len(nums_forward)): if nums_forward[index] < 4: del nums_forward[index] print(nums_forward) except IndexError as error: print("IndexError:", error, "| список в момент сбоя:", nums_forward) # IndexError: list index out of range | список в момент сбоя: [5, 8] nums_backward = [2, 5, 1, 8, 3] for index in range(len(nums_backward) - 1, -1, -1): if nums_backward[index] < 4: del nums_backward[index] print(nums_backward) # [5, 8] — корректный результат