Основы предсказания вывода Python
При анализе кода на Python 3 важно понимать, как интерпретатор выполняет инструкции, какие типы данных используются и какие операции вызывают побочные эффекты. Ниже приведен общий метод предсказания вывода и несколько типичных ситуаций с примерами.
Основные принципы предсказания вывода
Эффективный способ: последовательный разбор кода
Для точного предсказания вывода необходимо мысленно выполнить код шаг за шагом, фиксируя значения переменных и результат каждой операции. Следует знать приоритет операторов, правила преобразования типов и особенности изменяемых объектов. Пример:
a = 5
b = a + 2
print(b)Python выводит python (объяснение вывода строки 'python')
7
что выведет python 3 (что выведет python 3)
Здесь сначала присваивается 5 переменной a, затем вычисляется a+2 (7) и результат сохраняется в b, после чего выводится 7.
Типичная ошибка: забыть, что print не возвращает значение, а выводит на экран. Например, x = print(3) даст x = None.
Как определить результат выполнения арифметических операций с разными типами?
print(10 / 3)
print(10 // 3)
print(10 % 3)3.3333333333333335 3 1
Деление / возвращает float, целочисленное деление // отбрасывает дробную часть, % дает остаток. Обратите внимание на погрешность float.
Проблема: неявное преобразование int во float может привести к неожиданной точности. Например, 0.1 + 0.2 == 0.3 даст False.
Что выведет код при конкатенации строк и умножении строки на число?
s = "Hello"
print(s + " World")
print(s * 3)Hello World HelloHelloHello
Строки складываются, умножение повторяет строку указанное число раз. Строки неизменяемы, операции создают новые объекты.
Ошибка: попытка сложить строку с числом без явного преобразования вызывает TypeError. Например, "Age: " + 25 вызовет ошибку.
Как предсказать результат при изменении списка внутри цикла?
lst = [1, 2, 3]
for i in lst:
lst.append(i*10)
print(lst[:6])[1, 2, 3, 10, 20, 30]
Цикл итерирует по исходному списку, но во время итерации список растет. Однако итерация останавливается после достижения конца исходного списка (длина фиксируется в начале цикла). Выводятся только первые 6 элементов.
Проблема: изменение списка во время итерации может привести к бесконечному циклу или пропуску элементов. Рекомендуется создавать копию.
Как работает короткое замыкание в логических операторах and и or?
print(False and 1/0)
print(True or 1/0)
print(1 and 2 or 3)False True 2
Оператор and возвращает первый ложный операнд или последний, если все истинны. or возвращает первый истинный. Выражение 1/0 не вычисляется из-за короткого замыкания.
Ошибка: неверное предположение, что логические операторы всегда возвращают bool. Они возвращают значение одного из операндов.
Почему функции, созданные в цикле, дают неожиданный вывод?
funcs = []
for i in range(3):
funcs.append(lambda: i)
print([f() for f in funcs])[2, 2, 2]
Лямбда-функции захватывают переменную i по ссылке, а не по значению. К моменту вызова цикла i уже равна 2. Для фиксации значения нужно использовать аргумент по умолчанию:
funcs = [lambda x=i: x for i in range(3)]
print([f() for f in funcs])[0, 1, 2]
Проблема: позднее связывание замыканий – частая ловушка. Решение: передавать значение как аргумент по умолчанию или использовать functools.partial.
Что выведет код при использовании глобальных и локальных переменных?
x = 10
def func():
print(x)
x = 5
func()UnboundLocalError: local variable 'x' referenced before assignment
При наличии присваивания x внутри функции Python считает x локальной переменной, но print пытается обратиться к ней до присваивания. Исправление: объявить global x или не присваивать.
Ошибка: предположение, что если переменная определена вне функции, то она доступна внутри без global. Это верно только для чтения; присваивание создает локальную.
Как ведет себя оператор is при сравнении целых чисел?
a = 256
b = 256
print(a is b)
c = 257
d = 257
print(c is d)True False
Python кэширует небольшие целые числа (обычно от -5 до 256) для оптимизации. Для чисел вне этого диапазона is может дать False, даже если значения равны. Следует использовать == для сравнения значений.
Проблема: путаница между is и ==. is проверяет идентичность объектов, == проверяет равенство значений.
Расширенные примеры предсказания вывода
Ниже приведены более сложные или неочевидные случаи, которые помогут глубже понять механизмы Python.
Пример 1: Распаковка кортежей со звездочкой
a, *b, c = (1, 2, 3, 4)
print(a, b, c)1 [2, 3] 4
Звездочка собирает все промежуточные элементы в список. Если элементов недостаточно, возникнет ошибка.
Пример 2: Оператор моржа (walrus)
print(x := 5, x * 2)5 10
Оператор := присваивает значение переменной прямо внутри выражения и возвращает его. Выражение x := 5 присваивает x значение 5 и возвращает 5.
Пример 3: Изменяемый аргумент по умолчанию
def add_to_list(item, lst=[]):
lst.append(item)
return lst
print(add_to_list(1))
print(add_to_list(2))
print(add_to_list(3, []))[1] [1, 2] [3]
Аргумент по умолчанию создается один раз при определении функции. Последующие вызовы используют тот же список, если не передан новый. В третьем вызове передан новый пустой список, поэтому результат [3].
Пример 4: Цепочка сравнений
print(1 < 2 < 3)
print(1 < 3 > 2)
print(1 < (2 < 3))True True False
Цепочка a < b < c эквивалентна a < b and b < c. 1 < 3 > 2 – это 1 < 3 and 3 > 2. Последнее выражение: (2<3) равно True, затем 1 < True – Python преобразует True в 1, получается 1<1 -> False.
Пример 5: Генератор и его потребление
gen = (x**2 for x in range(3))
print(list(gen))
print(list(gen))[0, 1, 4] []
Генератор можно обойти только один раз. После первого list(gen) генератор истощается, второй вызов дает пустой список.
Пример 6: Срез списка с присваиванием
lst = [1, 2, 3, 4, 5]
lst[1:3] = [100, 200]
print(lst)[1, 100, 200, 4, 5]
Срез с присваиванием заменяет указанный диапазон элементов новыми. Количество элементов может отличаться.
Пример 7: Dict comprehension с условием
d = {i: i*2 for i in range(5) if i % 2 == 0}
print(d){0: 0, 2: 4, 4: 8}Создается словарь, где ключи – четные числа от 0 до 4, значения – удвоенные ключи.
Пример 8: Вложенные списковые включения
matrix = [[1, 2], [3, 4]]
flattened = [num for row in matrix for num in row]
print(flattened)[1, 2, 3, 4]
Порядок циклов: сначала внешний for row, затем внутренний for num. По сути эквивалентно двум вложенным циклам.
Пример 9: Метод join и split
s = "a,b,c"
print(s.split(','))
print('-'.join(['x', 'y', 'z']))['a', 'b', 'c'] x-y-z
split разбивает строку по разделителю, возвращая список. join объединяет список строк через указанный разделитель.
Пример 10: lambda и map с фильтром
nums = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, nums)))
print(squared)[4, 16]
filter отбирает четные числа, map применяет квадрат, list преобразует итератор в список.