К содержанию

Модуль 5 · Урок 5.1

Функции: def, return и область видимости

Своё действие с именем: параметры и аргументы, return против print, локальные имена и изменение переданных объектов.

8 задач

Ты уже вызывал готовые действия: print, len, max. Когда одно и то же вычисление нужно в нескольких местах, его хочется записать один раз и вызывать по имени. Функция — именованное действие: ей передают входные данные, она возвращает результат туда, где её вызвали.

Начнём с площади прямоугольника 3 × 5, затем разберём, чем return отличается от print, почему имена внутри функции не мешают внешним и что бывает, когда функция получает список.

Функция

Функция даёт имя действию с входными данными и результатом. Посчитаем площадь прямоугольника функцией:

def rectangle_area(width, height):    return width * height area = rectangle_area(3, 5)print(area)print(rectangle_area(2, 10))
15 20

def создаёт функцию. rectangle_area — её имя. width и height — параметры, то есть локальные имена для входных данных. В вызове числа 3 и 5 — аргументы, конкретные переданные значения.

При выполнении def тело не запускается: оно выполняется при каждом вызове. Поэтому одну функцию можно вызвать много раз с разными аргументами: второй вызов дал 20. Вот что происходит при вызове rectangle_area(3, 5):

Аргументы 3 и 5 становятся значениями параметров width и height, тело вычисляет 15, а return отдаёт его в строку вызова — она становится area = 15.
Тонкости:где писать определение

Определение должно выполниться раньше вызова. Функция может вызывать другую функцию, определённую ниже в файле, — если к моменту реального вызова обе def уже выполнены. Проще всего писать все функции в начале файла.

return и print

return передаёт значение месту вызова, print только показывает текст. return завершает текущий вызов и передаёт значение тому месту, где он произошёл. print показывает текст, но не делает этот текст возвращаемым результатом. Нажми «Запуск» и пройди программу по шагам:

def square(number):    return number * number result = square(4) + square(3)print(result)
25

При вызове у функции появляются свои переменные — рамка «вызов square(4)» внутри программы; после return она исчезает, а значение возвращается в строку, где был вызов.

Если вместо return поставить print, квадраты будут видны на экране, но сложение результатов вызовов не сработает: оба вызова вернут None.

def square(number):    print(number * number) result = square(4)print(result)
16 None

Функция без выполненного return со значением возвращает None. Запись return без выражения тоже возвращает None. Инструкции после выполненного return в этом вызове уже не исполняются.

def describe_sign(number):    if number > 0:        return "положительное"    if number < 0:        return "отрицательное"    return "ноль" print(describe_sign(-4), describe_sign(0))
отрицательное ноль

Ранний возврат позволяет последовательно исключить случаи без лишней вложенности: третий return выполняется, только если первые два не сработали.

Локальные имена

Имена внутри функции локальны: они не заменяют внешние. Область видимости — часть программы, в которой имя разрешается определённым способом. Параметры и присваивания внутри функции обычно создают локальные имена, существующие для данного вызова. Пройди программу по шагам:

x = 10 def calculate():    x = 3    return x + 1 print(calculate())print(x)
4 10

x = 3 появляется в рамке вызова calculate, а внешнее x в программе остаётся 10: внутреннее имя не заменяет внешнее.

Если внутри функции есть присваивание имени, Python обычно считает его локальным во всём теле. Поэтому попытка сначала прочитать его, а потом присвоить приводит к ошибке:

x = 10 def show():    print(x)    x = 3 show()
UnboundLocalError: cannot access local variable 'x' where it is not associated with a value

Строка x = 3 сделала x локальным во всей функции, и print(x) обратился к локальному имени, которое ещё не получило значения. Нужно внешнее значение — передай его параметром и верни новое.

Тонкости:порядок поиска имён, global и nonlocal

Имя Python ищет по уровням: сначала в текущем вызове, затем в окружающих функциях, затем в файле, затем среди встроенных имён — сокращённо LEGB. global и nonlocal позволяют функции менять имя внешнего уровня, но программу с ними труднее читать.

У if и for нет собственной отдельной области имён, как у функции. Переменная, присвоенная внутри выполненной ветки, остаётся доступной после неё в окружающей области.

Параметр и объект

Присвоить параметру и изменить переданный объект — разные действия. Параметр — ещё одно имя того объекта, который передали. Нажми «Запуск» и пройди программу по шагам:

def rebind(items):    items = [99] def mutate(items):    items.append(99) a = [1, 2]rebind(a)print(a)mutate(a)print(a)
[1, 2] [1, 2, 99]

В rebind заменяется локальная связь имени. В mutate меняется тот же список, на который ссылается внешнее a. Это та же модель «имя — ссылка на объект», что и в уроке о списках: параметр — ещё одно имя того же списка. Изменение данных вне возвращаемого результата называют побочным эффектом. Вывод и запись файла — тоже побочные эффекты.

Пока выполняется rebind, стрелки от a и items сначала ведут к одному списку, а после items = [99] — к разным. В mutate обе стрелки ведут к одному списку, и append меняет именно его.

В описании функции полезно явно указывать, меняет ли она переданную коллекцию. Если изменение не нужно, создавай отдельный результат.

Итог

Теперь ты умеешь давать вычислению имя и вызывать его сколько угодно раз. Вот что из урока понадобится дальше.

  • def имя(параметры):Создаёт функцию; тело выполняется только при вызове.
  • Аргумент и параметрВ вызове rectangle_area(3, 5) 3 и 5 — аргументы, они становятся значениями параметров.
  • returnОтдаёт значение в место вызова и завершает вызов; без него функция возвращает None.
  • printТолько показывает текст, результатом функции не становится.
  • Локальные именаПрисваивание внутри функции создаёт своё имя; чтение до присваивания — UnboundLocalError.
  • Параметр-списокappend меняет переданный список, присваивание параметру — нет.

Проверьте себя

Ответь сначала сам, потом открой ответ.

Вопрос 1

В вызове rectangle_area(4, 7) что здесь аргументы, а что параметры? Что вернёт вызов?

ОтветСкрыть ответ
Ответ

4 и 7 — аргументы, width и height — параметры. Вызов вернёт 28.

Вопрос 2

Что напечатает программа?

def double(n):    print(n * 2) result = double(5)print(result)
ОтветСкрыть ответ
Ответ

10 и None: функция печатает 10, но ничего не возвращает.

Вопрос 3

Что вернёт describe_sign(5) из урока и какие строки функции при этом выполнятся?

ОтветСкрыть ответ
Ответ

«положительное»: первая проверка истинна, первый return завершает вызов, и остальные строки не выполняются.

Вопрос 4

Вне функции задано count = 0, а функция bump() выполняет count = count + 1. Что произойдёт при вызове bump() и почему?

ОтветСкрыть ответ
Ответ

UnboundLocalError: присваивание делает count локальным во всём теле, а правая часть читает его раньше, чем оно получило значение.

Вопрос 5

Функция add_zero(items) выполняет items.append(0). Чему равен a после a = [1] и add_zero(a)?

ОтветСкрыть ответ
Ответ

[1, 0]: параметр items — ещё одно имя того же списка, и append меняет его.

Практика

Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.

Напиши функцию: периметр прямоугольника

Напиши функцию perimeter(width, height), которая возвращает периметр прямоугольника. Вызови её для 3 и 5, напечатав результат и запиши, что напечатала программа.

Введите число.

Что напечатает программа?

def f(n):    print(n * 2) r = f(3)print(r)

Запишите напечатанные строки через пробел в порядке вывода.

Что напечатает программа?

def describe_sign(number):    if number > 0:        return "положительное"    if number < 0:        return "отрицательное"    return "ноль" print(describe_sign(7))print(describe_sign(-0.5))

Запишите напечатанные строки через пробел в порядке вывода.

Что напечатает программа?

x = 1 def g():    x = 2    return x print(g(), x)

Запишите напечатанные строки через пробел в порядке вывода.

Что напечатает программа?

def h(a):    a.append(0)    a = [5]    a.append(1) lst = [9]h(lst)print(lst)

Запишите список так, как его печатает Python.

Напиши функцию: последняя цифра

Напиши функцию last_digit(n), которая возвращает последнюю цифру модуля числа. Вызови её так: print(last_digit(47) + last_digit(-123) + last_digit(50)) и запиши, что напечатала программа.

Введите число.

Напиши функцию: чётные числа

Напиши функцию is_even(n), которая возвращает True или False, и с её помощью посчитай чётные числа в списке [3, 8, 0, -6, 7]. Вызови её в цикле по списку, напечатав количество и запиши, что напечатала программа.

Введите число.

Напиши функцию: наибольшее из трёх

Напиши функцию max_of_three(a, b, c) без max. Вызови её для −5, −2 и −9, напечатав результат и запиши, что напечатала программа.

Введите число.