В этой статье мы просто приведём практические примеры работы функций в Python. Рассмотрим базовые, встроенные и пользовательские функции, а также функции с параметрами, возвращаемым значением и типом данных.
Функции в Python представляют собой фрагменты кода в блоке, который имеет назначенное имя. Функции принимают ввод, осуществляют вычисления либо какое-нибудь действие и возвращают вывод. И, разумеется, функции упрощают работу с кодом, делая возможным его повторное использование.
Базовые функции Python
Давайте рассмотрим пример функции Python, принимающей 2 параметра, а также вычисляющей сумму и возвращающей вычисленное значение:
#определяем и объявляем функцию def calculate_sum(a,b): sum = a+b return sum #инструкция, приведённая ниже, называется вызовом функции print(calculate_sum(2,3)) # 5Кроме того, в Python есть встроенные и пользовательские функции.
Пользовательские функции Python
Объявление пользовательской функции осуществляется с применением ключевого слова def. При этом оно должно сопровождаться именем пользовательской функции:
def calculate_si_amount(principal, rate, time): interest = (principal*rate*time)/100 return principal+interestВ данной функции окончательная сумма может быть рассчитана посредством использования простого процента к основной сумме. Именем функции является Calculate_si_amount. Что касается principal, time и rate — то это параметры, а функция возвращает рассчитанные данные.
Для пользовательской функции можно не принимать возвращаемые значения и параметры. На нижеследующем примере мы видим пользовательскую функцию, не принимающую никаких параметров, зато возвращающую данные.
from random import seed, random from random import random def generate_random_number(): seed(10) return random()Встроенные функции Python
В Python существует много встроенных функций. Одна из наиболее часто используемых — print(). Её работа чрезвычайно проста:
print("Всем привет") print(len("Меня зовут Андрей"))Ещё популярны такие функции, как len(),abs(), sum(), str(), int() и другие.
Параметры функции в Python
В языке программирования Python функция может иметь параметры по умолчанию:
def multiply(a, b=10): return a*b multiply(12) # 120 multiply(2, 3) # 6 multiply(b=9) # Ошибка: None*9 недопустимоВ вышеописанной функции, когда пользователь не задает 2-й параметр b, он предполагает, что параметр равен 10, однако при этом нужно предоставить 1-й параметр.
Неизвестное количество параметров в функции Python
Когда в функции, допустим, четыре параметра, а для второго параметра определено значение по умолчанию, то третьему и четвёртому параметрам тоже необходимо присвоить значение по умолчанию.
Когда число параметров неизвестно, тогда в определение функции в качестве одного из параметров добавляется *args. Данный параметр ожидает кортеж. В нашем случае звёздочка (*) очень важна, т. к. название args просто является соглашением, то есть можно дать любое другое имя.
def calculate_sum(a, *args): sum = a for i in args: sum += i return sum calculate_sum(10) # 10 calculate_sum(10, 11, 12) # 33 calculate_sum(1, 2, 94, 6, 2, 8, 9, 20, 43, 2) # 187Так же **kwargs ожидает словарь в качестве параметра.
def print_names(f1, l1, **kwargs): print(f1, l1, end=' ') for key in kwargs: print(key, kwargs[key], end=' ') print_names("andrey", "master") print_names("andrey", "master", alex="john", leon="elene") # andrey master andrey master alex john leon elene
Обратите внимание, что фрагмент выше имеет ссылку на цикл for.
Тип данных для возвращаемого значения и параметров в Python
Определение типов данных для параметров функции в Python может быть полезным:
def prime_numbers(x:int) -> (int, list): l=[] for i in range(x+1): if checkPrime(i): l.append(i) return len(l), lВ нашем примере определение функции указывает, что нужен 1 параметр типа int и вернёт два значения типа list и int соответственно.
Возвращаемое значение функции в Python
Язык программирования Python даёт возможность функции возвращать несколько значений.
def prime_numbers(x): l=[] for i in range(x+1): if checkPrime(i): l.append(i) return len(l), l no_of_primes, primes_list = prime_numbers(100)В нашем случае возвращаются 2 значения. Если данная функция вызывается, то возвращаемые значения сохраняются одновременно в 2-х переменных. Если же функция не возвращает ничего, то она неявно возвращает None.
Понятие функции в Python
Несмотря на то, что формально функции мы еще не рассматривали, некоторое понятие о них мы уже имеем. Ведь ранее мы не только использовали готовые встроенные функции и различные методы
типов, но еще и определяли простейшие собственные функции. Теперь же настало время рассмотреть этот вопрос более подробно. Итак.
Функция в Python (от англ. function) –
это блок программного кода на языке Python, который определяется один раз и далее может быть использован многократно.
Каждая хорошо спроектированная функция по сути решает какую-то одну конкретную задачу, что дает возможность программистам разбивать сложную систему на достаточно простые и легко управляемые
части. При этом достаточно всего лишь один раз определить функцию и далее ее можно будет использовать практически в любом месте программы, зачастую заменяя довольно объемные избыточные копии
блоков одного и того же программного кода вызовами созданной ранее функции. Как следствие, при необходимости внесения изменений в код, делать это придется уже не во многих местах программы,
а только в одном – в теле самой функции (см. пример №1).
Таким образом функции можно смело назвать основными программными структурами языка Python, которые позволяют существенно уменьшить время и трудозатраты на
разработку приложений, а также их дальнейшее сопровождение за счет следующих преимуществ:
- дают возможность разбивать сложную систему на небольшие и легко управляемые части, каждая из которых может разрабатываться отдельно,
- обеспечивают многократное использование программного кода,
- уменьшают его избыточность,
- повышают читабельность исходного кода,
- упрощают его редактирование.
Создание и вызов функций в Python
Для создания функций в Python предназначена инструкция def, которая создает объект функции и связывает его с именем.
В общем виде инструкция имеет следующий формат:
def <Имя функции>(arg_1, arg_2, ..., arg_n):
<Тело функции>
return <Возвращаемое значение>
Как видим, строка заголовка инструкции определения функции начинается с ключевого слова def, затем через пробел указывается имя функции и следующие за ним
обязательные круглые скобки, внутри которых через запятую перечисляются передаваемые функции аргументы (их еще называют параметрами). Можно определять функции и без аргументов, но круглые
скобки при этом все равно нужно указывать. Завершается строка заголовка обязательным двоеточием. Далее следует тело функции, представляющее собой обычный блок программного кода, который будет
выполняться каждый раз при вызове функции. При этом, если тело функции состоит из одной простой инструкции, его можно записывать вслед за двоеточием в строке заголовка. В более сложных
случаях инструкции тела функции записываются с отступами в соответствии с общепринятыми правилами.
Помимо обычных инструкций в любом месте тела функции может содержаться необязательная инструкция return, которая предназначена для выхода из функции и
возврата результата вызывающей программе в месте вызова функции. В случае отсутствия инструкции return функция будет завершать свою работу по достижении
потоком управления конца тела функции. Однако технически функция все равно будет неявно возвращать результат в виде объекта None, который обычно просто
игнорируется.
Также для использования в функциях предназначена специальная инструкция yield, которая превращает
обычную функцию в функцию-генератор. Однако здесь мы заострять внимание на ней не будем, поскольку данный вопрос был подробно рассмотрен нами в предыдущем параграфе.
После того, как функция будет определена, ее можно начинать использовать, многократно вызывая практически в любое время и в любом месте скрипта. Осуществляются такие вызовы при помощи имени
функции и круглых скобок с передаваемыми в функцию значениями аргументов, которые также должны перечисляться через запятую (см. пример №1).
Код
Результат
pythonCodes
# Функция определяется один раз.
def pow_func(x, y):
# Возводим x в степень y.
p = x**y
# Возвращаем результат вызывающей программе.
return p
# После создания функцию можно вызывать.
res_1 = pow_func(3, 3)
# Получим 27, т.е. 3 в кубе.
print('pow_func(3, 3) ->', res_1, end='\n\n')
# При этом вызовы можно осуществлять многократно.
res_2 = pow_func(4, 2)
# Выведет 16, т.е. 4 в квадрате.
print('pow_func(4,2) ->', res_2)
pow_func(3, 3) -> 27
pow_func(4,2) -> 16
Пример №1. Создание и вызов функций в Python (часть 1).
Следует иметь в виду, что в отличие от некоторых других языков программирования в Python функции становятся доступными для использования только после того,
как до них дойдет поток выполнения программы. Если попытаться вызвать функцию до ее определения в коде программы, интерпретатор возбудит исключение. При этом создавать функции можно
практически везде, где могут появляться инструкции, включая условные инструкции и определения других функций (см. пример №2).
Код
Результат
pythonCodes
# Вызывать ф-цию можно только после определения.
# local variable func_1 referenced before assignment.
# func_1()
# Я ввел 1 (попробуйте ввести, например, -1).
n = int(input())
# Определяем ф-цию в зависимости от условия.
if n >= 0:
# Определяем 1-ю функцию.
def func_1(): print('func_1 определена!')
else:
# Определяем 2-ю функцию.
def func_2(): print('func_2 определена!')
# Вызов разрешен, т.к. func_1 уже определена.
# Выведет 'func_1 определена!'.
func_1()
# Вызов запрещен, т.к. func_2 определена не была.
# local variable 'func_2' referenced before assignment.
func_2()
func_1 определена!
local variable 'func_2' referenced before assignment
Пример №2. Создание и вызов функций в Python (часть 2).
Аргументы функций в Python
Как было сказано выше, в случаях, когда функция определяется с аргументами, их имена перечисляются через запятую в круглых скобках после имени определяемой функции. Далее, в моменты вызовов
функции, все определенные имена автоматически становятся ее локальными переменными, которым присваиваются переданные в функцию значения. При этом по умолчанию сопоставление аргументов
производится слева направо в соответствии с их позициями, а самих аргументов допускается передавать ровно столько, сколько имен было указано в заголовке функции при ее определении
(см. пример №3).
Код
Результат
pythonCodes
# Определяем и вызываем функцию без аргументов.
def func_1(): print('Ok')
# Выведет 'Ok'.
func_1()
# ...func_1() takes 0 positional arguments but 1 was given.
# func_1(57)
# Функция с тремя позиционными аргументами.
def func_2(a, b, c): return a + b + c
# При вызове должны быть переданы все 3 аргумента.
print(func_2(2, 33, 87))
# ...func_2() missing 1 required positional argument: c.
# func_2(4, 8)
Ok
122
Пример №3. Передача аргументов функциям (часть 1).
Как видим, несоответствие количества передаваемых аргументов при вызове функции с количеством указанных имен аргументов при ее определении однозначно приводит к ошибке. Поэтому в ситуациях,
когда точное количество передаваемых аргументов заранее неизвестно, следует определять и вызывать функции, используя один из доступных в Python специальных
режимов сопоставления аргументов, которые мы сейчас и перечислим.
-
def func(arg_1, arg_2, …): – рассмотренный нами стандартный случай определения функции. Во время вызова такая функция ожидает получить строго
определенное количество аргументов, сопоставление которых будет происходить либо по их позициям, либо по именам. При этом в случае использования в вызове функции именованных аргументов
их разрешается указывать в любом порядке, но только после того, как будут переданы все имеющиеся позиционные аргументы, которые в любом случае будут сопоставляться как и положено, т.е.
слева направо в соответствии с их позициями (см. пример №4).
Код
Результат
pythonCodes
# Определяем функцию с тремя позиционными аргументами.
def func(x, y, z): return x/y + z
# Передаем три позиционных аргумента.
# Выведет func(8, 2, 5) -> 9.0.
print('func(8, 2, 5) ->', func(8, 2, 5))
# Для некоторых арг-тов указываем имена, но сперва в любом
# случае должны идти позиционные арг-ты. Выведет 9.0.
print('func(x=8, y=2, z=5) ->', func(x=8, y=2, z=5))
print('func(y=2, x=8, z=5) ->', func(y=2, x=8, z=5))
print('func(z=5, x=8, y=2) ->', func(z=5, x=8, y=2))
print('func(8, y=2, z=5) ->', func(8, y=2, z=5))
print('func(8, z=5, y=2) ->', func(8, z=5, y=2))
print('func(8, 2, z=5) ->', func(8, 2, z=5))
# Именованные аргументы разрешается передавать только
# после позиционных. Так что везде получаем ошибки.
# print(func(x=8, 2, 5))
# print(func(8, y=2, 5))
# print(func(x=8, 2, z=5))
func(8, 2, 5) -> 9.0
func(x=8, y=2, z=5) -> 9.0
func(y=2, x=8, z=5) -> 9.0
func(z=5, x=8, y=2) -> 9.0
func(8, y=2, z=5) -> 9.0
func(8, z=5, y=2) -> 9.0
func(8, 2, z=5) -> 9.0
Пример №4. Стандартная передача аргументов функциям.
-
def func(arg_1, arg_2, …, arg_v_1=val_1, arg_v_2=val_2, …) – эта форма записи используется для определения функций, у которых
предусмотрены аргументы со значениями по умолчанию. Перечисляются такие аргументы после обычных позиционных аргументов (если они имеются), а их наличие позволяет
вызывать функцию без передачи им значений, т.к. в таком случае будут просто использованы значения по умолчанию. Во всем остальном для таких функций справедливы
правила стандартного режима передачи аргументов (см. пример №5).
Код
Результат
pythonCodes
# Укажем для одного аргумента значение по умолчанию.
def func(x, y=1): return x/y
# Везде получим 4.0, т.к. 8/2 = 4.0.
print('func(8, 2) ->', func(8, 2))
print('func(x=8, y=2) ->', func(x=8, y=2))
print('func(8, y=2) ->', func(8, y=2), end='\n\n')
# Ошибка, т.к. первыми должны передаваться
# позиционные аргументы и только потом именованные.
# print('func(x=8, 2) ->', func(x=8, 2))
# Аргумент со значением по умолчанию можно не указывать.
# Везде получим 2.0, т.к. 2/1 = 2.0.
print('func(2) ->', func(2))
print('func(x=2) ->', func(x=2), end='\n\n')
# ...missing 1 required positional argument: x.
# print(func(y=2))
# print(func())
func(8, 2) -> 4.0
func(x=8, y=2) -> 4.0
func(8, y=2) -> 4.0
func(2) -> 2.0
func(x=2) -> 2.0
Пример №5. Передача аргументов со значениями по умолчанию.
-
def func(arg_1, …, arg_n, *args) – при вызовах функции, определенной таким способом, все дополнительные аргументы будут объединяться в кортеж с
именем args. Если передаваемых аргументов будет меньше n, будет вызвана ошибка. Сама запись
*args должна идти после перечисления всех позиционных аргументов, включая и позиционные аргументы со значениями по умолчанию
(см. пример №6).
Код
Результат
pythonCodes
# 1-й аргумент будет присвоен x, а все
# остальные будут собраны в кортеж y.
def func(x, *y): return x + sum(y)
# Получим 5, т.к. 5 + sum(()) = 5.
print('func(5) ->', func(5))
# Получим 8, т.к. 5 + sum((3,)) = 8.
print('func(5, 3) ->', func(5, 3))
# Получим 15, т.к. 5 + sum((3, 7)) = 15.
print('func(5, 3, 7) ->', func(5, 3, 7))
# Ошибка, т.к. х не имеет значения по умолчанию.
# print(func())
func(5) -> 5
func(5, 3) -> 8
func(5, 3, 7) -> 15
Пример №6. Сбор аргументов функции в кортеж.
-
def func(arg_1, …, arg_n, *args, named_arg_1, …, named_arg_m) – здесь также все дополнительные аргументы будут объединяться в
кортеж с именем args, но при этом все аргументы named_arg_1, …, named_arg_m должны будут
передаваться функции только именованными. Если собирать аргументы в кортеж не требуется, но функция проектируется для именованных аргументов, разрешается использовать
синтаксис def func(arg_1, …, arg_n, *, named_arg_1, …, named_arg_m), где все аргументы после звездочки, опять же, должны будут
передаваться функции только именованными (см. пример №7).
Код
Результат
pythonCodes
# z определяем именованным аргументом.
def func(x, *y, z): return x + sum(y) + z
# Получим 15, т.к. 5 + sum((7,)) + 3 = 15.
print('func(5, 7, z=3) ->', func(5, 7, z=3))
# Получим ошибку, т.к. именованные аргументы
# должны передаваться только после позиционных!
# print(func(5, z=7, 3))
# ...missing 1 required keyword-only argument: 'z'.
print('func(5, 7, 3) ->', func(5, 7, 3))
func(5, 7, z=3) -> 15
main..func() missing 1 required keyword-only argument: 'z'
Пример №7. Определение функции с именованными аргументами.
-
def func(arg_1, …, arg_n, *args, named_arg_1, …, named_arg_m, **named_args) – это наиболее общая форма определения функции с позиционными и
именованными аргументами: при вызовах такой функции все дополнительные позиционные аргументы будут объединяться в кортеж с именем args, а все
дополнительные именованные аргументы будут объединяться в словарь с именем named_args (см. пример №8).
Код
Результат
pythonCodes
# Собираем именованные аргументы в словарь.
def func(x, *, y, **z): print(x, y, z)
# Выведет 1 2 {'a': 3, 'b': 4}.
func(1, y=2, a=3, b=4)
# Выведет 1 2 {'z': 3, 'f': 4}.
func(1, y=2, z=3, f=4)
# Выведет 1 2 {}.
func(1, y=2)
1 2 {'a': 3, 'b': 4}
1 2 {'z': 3, 'f': 4}
1 2 {}
Пример №8. Сбор именованных аргументов в словарь.
Следует помнить, что форма записи arg=val в определении функции означает, что данный аргумент определяется со значением по умолчанию, а в вызове функции
запись представляет собой передачу аргумента по имени. Кроме того, формы записей *args и **named_args также имеют разный
смысл для определения и вызова функции: в первом случае они собирают аргументы в кортеж и словарь, а во втором случае указывают интерпретатору на необходимость распаковки последовательностей
в отдельные аргументы и пары (см. пример №9).
Код
Результат
pythonCodes
# Собираем аргументы в последовательности.
def func(*x, **y): print(x, y)
# Вывело (1, 2) {'a': 1, 'b': 2}.
func(1, 2, a=1, b=2)
# А здесь последовательности будем распаковывать.
def func(x, y): print(x, y)
# Равносильно func(1, 2). Вывело 1, 2.
func(*[1, 2])
# Равносильно func(x=1, y=2). Вывело 1, 2.
func(**{'x': 1, 'y': 2})
# Ошибка, т.к. мы определили имена x и y.
# ...got an unexpected keyword argument a.
# func(**{'a': 1, 'b': 2})
(1, 2) {'a': 1, 'b': 2}
1 2
1 2
Пример №9. Передача аргументов функциям (часть 2).
При первом прочтении информация о возможных режимах сопоставления аргументов может показаться несколько запутанной, но это только на первый взгляд. На самом деле все оказывается значительно
проще в особенности, если придерживаться пары простых правил:
-
в заголовке определяемой функции аргументы должны указываться в следующем порядке: позиционные аргументы (arg_1, arg_2, …),
позиционные аргументы со значениями по умолчанию (arg_v_1=val_1, arg_v_2=val_2, …),
аргумент со звездочкой для сбора дополнительных позиционных аргументов в кортеж (*args), именованные аргументы
(named_arg_1, named_arg_2, …), именованные аргументы со значениями по умолчанию
(named_arg_1=val_1, named_arg_2=val_2, …) и аргумент с двумя звездочками для сбора дополнительных именованных аргументов в словарь
(**named_args); -
при вызове функции порядок передачи аргументов должен быть следующим: обычные позиционные аргументы (arg_1, arg_2, …),
позиционные аргументы в форме *args (будут распакованы интерпретатором в значения arg_1, arg_2, …),
обычные именованные аргументы (named_arg_1=val_1, named_arg_2=val_2, …) и в самом конце именованные аргументы в форме
**named_args (будут распакованы интерпретатором в пары named_arg_1=val_1, named_arg_2=val_2, …).
Области видимости в Python
Как мы знаем, все имена в Python становятся доступны для использования только после того, как им будет присвоено какое-нибудь значение. Так вот, место, где
переменной присваивается значение, как раз и определяет область ее видимости. Но поскольку значения переменным могут быть присвоены в трех разных местах текущего модуля, то и областей
видимости выделяют тоже три.
-
Если присваивание переменной выполняется внутри инструкции def, переменная становится локальной для этой функции. Это касается как аргументов функции,
так и переменных, созданных в теле функции. Локальные переменные доступны только внутри своей функции, т.е. в текущей локальной области видимости, и
недоступны за ее пределами, включая другие локальные области видимости невложенных в нее функций. -
Если присваивание производится в пределах объемлющей инструкции def, переменная становится локальной для данной объемлющей функции и нелокальной для
данной вложенной функции. В общем случае такая переменная будет доступна внутри любой вложенной функции при отсутствии в ней локальной переменной с таким же именем. -
Если присваивание производится на верхнем уровне модуля (файла скрипта) за пределами всех инструкций def, переменная становится глобальной для
всего файла. В общем случае глобальная переменная будет доступна внутри любой функции текущего модуля при отсутствии в ней локальной переменной с
таким же именем (см. пример №10).
Код
Результат
pythonCodes
# Инициализировали глобальную переменную.
glob_var = 'glob_var'
# Выведет glob_var in global.
print(glob_var, 'in global', end='\n\n')
# Объявляем объемлющую функцию.
def func_1(loc_var):
# Локальная переменная для func_1.
loc_var_1 = 'loc_var_1'
# Выведет glob_var in func_1.
print(glob_var, 'in func_1')
# Выведет loc_var in func_1.
print(loc_var, 'in func_1')
# Выведет loc_var_1 in func_1.
print(loc_var_1, 'in func_1', end='\n\n')
# Объявляем вложенную функцию.
def func_2():
# Локальная переменная для func_2.
loc_var_2 = 'loc_var_2'
# Выведет glob_var in func_2.
print(glob_var, 'in func_2')
# Выведет loc_var_2 in func_2.
print(loc_var_2, 'in func_2')
# Выводим нелокальные для func_2.
# Выведет loc_var in func_2.
print(loc_var, 'in func_2')
# Выведет loc_var_1 in func_2.
print(loc_var_1, 'in func_2')
# Вызываем вложенную функцию func_2.
func_2()
# Вызываем объемлющую функцию.
func_1('loc_var')
glob_var in global
glob_var in func_1
loc_var in func_1
loc_var_1 in func_1
glob_var in func_2
loc_var in func_2
loc_var_1 in func_2
loc_var_2 in func_2
Пример №10. Области видимости в Python (часть 1).
Как видим, глобальная переменная glob_var доступна и в объемлющей функции, и во вложенной. Точно также локальная для функции
func_1 переменная loc_var доступна в локальной области вложенной функции func_2 (для
нее она является нелокальной переменной). Но если мы попробуем использовать локальные переменные в объемлющей или глобальной области видимости, то наверняка получим ошибку, т.к. локальные
переменные доступны только в своей области видимости (см. пример №11).
Код
Результат
pythonCodes
# Объявляем объемлющую функцию.
def func_1():
# Локальная переменная для func_1.
loc_var_1 = 'loc_var_1'
# Выведет loc_var_1 in func_1.
print(loc_var_1, 'in func_1')
# name 'loc_var_2' is not defined.
# print(loc_var_2)
# Объявляем вложенную функцию.
def func_2():
# Локальная переменная для func_2.
loc_var_2 = 'loc_var_2'
# Выведет loc_var_2 in func_2.
print(loc_var_2, 'in func_2', end='\n\n')
# Вызываем вложенную функцию func_2.
func_2()
# Вызываем объемлющую функцию.
func_1()
# name loc_var_1 is not defined.
# print(loc_var_1)
# name loc_var_2 is not defined.
# print(loc_var_2)
loc_var_1 in func_1
loc_var_2 in func_2
Пример №11. Области видимости в Python (часть 2).
Наличие обособленных локальных областей видимости и приоритет в них локальных переменных над глобальными и нелокальными переменными с теми же именами позволяет избежать неоднозначности и
конфликта имен. Этому содействует и невозможность использования для функций из одного и того же пространства имен локальных переменных одной функции в области видимости другой
(см. пример №12).
Код
Результат
pythonCodes
# Инициализировали глобальную переменную.
glob_loc_var = 'glob_loc_var in global'
# Выведет glob_loc_var in global.
print(glob_loc_var, end='\n\n')
# Объявляем 1-ю функцию.
def func_1():
# Локальная имеет приоритет над глоб. переменной.
glob_loc_var = 'glob_loc_var in func_1'
# Выведет glob_loc_var in func_1.
print(glob_loc_var)
# Локальная переменная для func_1.
loc_var_1 = 'loc_var_1 in func_1'
# Выведет loc_var_1 in func_1.
print(loc_var_1, end='\n\n')
# name 'loc_var_2' is not defined.
# print(loc_var_2)
# Объявляем 2-ю функцию.
def func_2():
# Локальная имеет приоритет над глоб. переменной.
glob_loc_var = 'glob_loc_var in func_2'
# Выведет glob_loc_var in func_2.
print(glob_loc_var)
# Локальная переменная для func_2.
loc_var_2 = 'loc_var_2 in func_2'
# Выведет loc_var_2 in func_2.
print(loc_var_2)
# name 'loc_var_1' is not defined.
# print(loc_var_1)
# Вызываем функции.
func_1()
func_2()
# name loc_var_1 is not defined.
# print(loc_var_1)
# name loc_var_2 is not defined.
# print(loc_var_2)
glob_loc_var in global
glob_loc_var in func_1
loc_var_1 in func_1
glob_loc_var in func_2
loc_var_2 in func_2
Пример №12. Области видимости в Python (часть 3).
Для случаев, когда возникает необходимость использования внутри функций именно глобальных или нелокальных переменных вместо локальных с теми же именами, в
Python предусмотрены инструкции global и nonlocal. Все что нужно – это просто
перечислить после ключевых слов требуемые имена через запятую (см. пример №13).
Код
Результат
pythonCodes
# Инициализируем глобальную переменную.
glob_var = 'Исходное значение glob_var'
# Выведет: Исходное значение glob_var.
print(glob_var)
# Объявляем объемлющую функцию.
def func_1():
# Будем использовать глобальную переменную.
global glob_var
# Изменяем значение глобальной переменной.
glob_var = 'glob_var изменена в func_1'
# Создаем локальную переменную для func_1.
loc_var = 'Исходное значение loc_var'
# Выведет: Исходное значение loc_var.
print(loc_var)
# Объявляем вложенную функцию.
def func_2():
# Будем использовать нелокальную переменную.
nonlocal loc_var
# Изменяем значение нелокальной переменной.
loc_var = 'loc_var изменена в func_2'
# Вызываем вложенную функцию func_2.
func_2()
# Выведет: loc_var изменена в func_2.
print(loc_var)
# Вызываем объемлющую функцию.
func_1()
# Выведет: glob_var изменена в func_1.
print(glob_var)
Исходное значение glob_var
Исходное значение loc_var
loc_var изменена в func_2
glob_var изменена в func_1
Пример №13. Использование инструкций global и nonlocal.
Из всего выше сказанного следует простое правило поиска имен: когда внутри функции выполняется обращение к неизвестному имени, интерпретатор пытается сначала отыскать его в локальной области
видимости, затем в локальной области любой объемлющей инструкции def или в выражении lambda и далее в глобальной области
видимости. Если искомого имени обнаружено не будет, интерпретатор выведет сообщение об ошибке.
Строго говоря, выделяют еще и встроенную область видимости, имена из которой автоматически добавляются в пространство имен глобальной области видимости текущего модуля. Сюда входят, например,
имена встроенных типов и функций. Поэтому говоря о глобальной области видимости без дополнительных оговорок, мы будем подразумевать и встроенную область видимости.
В конце пункта хотелось бы добавить, что получить сведения о локальных и глобальных переменных можно при помощи следующих встроенных функций:
-
globals() – возвращает словарь со значениями переменных из текущей глобальной области видимости модуля, в котором ключами служат имена этих переменных.
Стоит заметить, что функция возвращает такой словарь даже в тех случаях, когда она вызывается из локальных областей видимости функций и методов (см. пример
№14). -
locals() – обновляет и возвращает словарь со значениями переменных, использующихся в текущей локальной области видимости функции или метода, в котором
ключами служат имена этих переменных. Следует иметь в виду, что значения этого словаря изменять не стоит, т.к. изменённые значения все равно могут быть проигнорированы интерпретатором.
Кроме того, при использовании этой функции в глобальной области видимости модуля она вернет словарь с глобальными переменными также, как и функция
globals().
Код
Результат
pythonCodes
# Инициализируем 1-ю глобальную переменную.
glob_var_1 = 0.1
# Объявляем объемлющую функцию.
def func_1():
# Локальная переменная для func_1.
loc_var_1 = 1
# Словарь доступных локальных переменных для func_1.
print('func_1 locals:', locals())
# Словарь глобальных переменных.
print('globals:', globals())
# Объявляем вложенную функцию.
def func_2():
# Локальная переменная для func_2.
loc_var_2 = 2
# Словарь локальных переменных для func_2.
print('func_2 locals:', locals())
# Обновляем словарь локальных переменных для func_1.
print('func_1 locals:', locals())
# Вызываем вложенную функцию func_2.
func_2()
# Вызываем объемлющую функцию.
func_1()
# Словарь глобальных переменных.
print('globals:', globals())
# Выведет тоже самое.
print('globals:', locals())
# Инициализируем 2-ю глобальную переменную.
glob_var_2 = 0.2
# Обновляем словарь глобальных переменных.
print('globals:', globals())
func_1 locals: {'loc_var_1': 1}
globals: {..., 'glob_var': 0, 'func_1': <function func_1 at 0x000001C73638A200>}
func_1 locals: {'loc_var_1': 1, 'func_2': .func_1..func_2 at 0x00000204AF7A8700>}
func_2 locals: {'loc_var_2': 2}
globals: {..., 'glob_var': 0, 'func_1': <function func_1 at 0x000001C73638A200>}
globals: {..., 'glob_var': 0, 'func_1': <function func_1 at 0x000001C73638A200>}
globals: {..., 'glob_var': 0, 'func_1': <function func_1 at 0x000001C73638A200>, 'glob_var_2': 0.2}
Пример №14. Использование функций globals() и locals().
Как видно из примера, обе функции отображают в словаре только те переменные, которые известны интерпретатору на момент их вызова. Поэтому, если переменные объявляются в коде скрипта ниже
вызовов этих функций, их в возвращаемых словарях не будет.
lambda-выражения или анонимные функции в Python
Поскольку в Python инструкции не могут использоваться внутри выражений, для этих целей в язык была введена дополнительная возможность в виде специальных
конструкций, называемых lambda-выражениями и позволяющих создавать объекты функций там, где интерпретатор ожидает встретить выражение. Эти конструкции еще
называют анонимными или безымянными функциями, т.к. в отличие от инструкций def они хоть и создают функции, но не связывают
их с именами.
В общем виде lambda-выражение состоит из ключевого слова lambda, за которым перечисляются один или более аргументов
и после двоеточия записывается выражение, использующее эти аргументы:
lambda arg_1, arg_2, ..., arg_n: <выражение>
.
Здесь стоит заметить, что после двоеточия записывается именно выражение, а не блок инструкций. По сути такое выражение сродни тому, что мы помещаем в инструкцию
return при определении обычной функции. Это конечно же делает lambda-выражения менее универсальными по сравнению с
инструкциями def, но такая реализация предусмотрена намеренно, т.к. lambda-выражения предназначены для создания простых
функций, которые бы не усложняли выражения, в то время как инструкции def представляют собой инструменты для решения более сложных задач
(см. пример №14).
Код
Результат
pythonCodes
# Обычная инструкция def неявно сразу
# связывает объект функции с именем.
def func(x, y, z): return x + y + z
# Выведет 30.
print(func(5, 10, 15))
# Создаем объект анонимной ф-ции и
# явно присваиваем его переменной.
lambda_1 = lambda x, y, z: x + y + z
# Выведет 30.
print(lambda_1(5, 10, 15), end='\n\n')
# Используем значения по умолчанию.
lambda_2 = lambda x=8, y=4: x/y
# Выведет 2.0.
print('8/4 =', lambda_2())
# Выведет 3.0.
print('12/4 =', lambda_2(12), end='\n\n')
# Используем сбор аргументов в кортеж.
lambda_3 = lambda *nums: sum(nums)
# Выведет 5.
print('sum((2, 3)) =', lambda_3(2, 3), end='\n\n')
# Используем простейшую логику.
lambda_3 = lambda x, y: x/y if y != 0 else None
# Выведет 4.0.
print('20/5 =', lambda_3(20, 5))
# Выведет None.
print('20/0 =', lambda_3(20, 0))
# Ошибка: инструкции в теле lambda запрещены.
# lambda_4 = lambda x, y: if y != 0: x/y
30
30
8/4 = 2.0
12/4 = 3.0
sum((2, 3)) = 5
20/5 = 4.0
20/0 = None
Пример №14. Использование lambda-выражений (часть 1).
Как видим, lambda-выражения действительно очень похожи на обыкновенные функции: в них также действуют специальные режимы сопоставления аргументов,
имеется тело с кодом, а на выходе получается объект функции. Однако, повторимся, в теле lambda-выражений может находиться только одно единственное
выражение, в то время как использование даже простейших инструкций вообще запрещено. Кроме того, возвращаемый объект функции нужно либо сразу вызывать при помощи круглых скобок, либо
присваивать переменной явно. Зато lambda-выражения можно смело использовать внутри других выражений (см. пример №15).
Код
Результат
pythonCodes
# Список объектов функций.
li = [lambda x: x**2, lambda x: x**3]
# Выведет 9.
print('3*3 =', li[0](3))
# Выведет 27.
print('3*3*3 =', li[1](3), end='\n\n')
# Создание и вызов внутри выражения.
expr = 5 + (lambda x, y: x**y)(5, 2)
# Выведет 30.
print(expr)
# Вложенное lambda-выражение (но зачем усложнять?).
expr = (lambda x: 7 + (lambda a, b: a**b)(x, 2))(5)
# Выведет 32.
print(expr)
3*3 = 9
3*3*3 = 27
30
32
Пример №15. Использование lambda-выражений (часть 2).
В нашем примере вложенное lambda-выражение, как и положено вложенным функциям, имеет доступ к переменной x в объемлющем
lambda-выражении. Но выглядит код явно замысловато! Поэтому в интересах удобочитаемости кода лучше такие трюки не использовать.
Рекурсивные функции в Python
Рекурсивная функция – это функция, которая содержит код вызова самой себя в целях организации циклического
процесса.
Рекурсивные функции являются вполне обычными функциями со всеми присущими им особенностями. Разница лишь в том, что внутри тела такие функции содержат вызов самой себя. Звучит это
как-то странно и непонятно, в особенности для начинающих программистов, поэтому стоит сразу же обратиться к практике. В качестве примера, давайте рассмотрим использование рекурсивной функции
в ходе решения классической задачи нахождения факториала числа (см. пример №16).
Код
Результат
pythonCodes
# Обычное объявление функции.
def recursive_func(n):
# Условие завершения рекурсий.
if n == 1:
# Возвращаем 1.
return n
# Иначе
else:
# используем вызов самой себя,
# уменьшив значение аргумента на 1.
return n*recursive_func(n-1)
# Выводим 3!, т.е. 6.
print(recursive_func(3))
# Выводим 5!, т.е. 120.
print(recursive_func(5))
6
120
Пример №16. Рекурсивные функции в Python.
В нашем примере код рекурсивной функции содержит условную инструкцию if, которая останавливает рекурсии (вызовы самой себя) только в том случае, когда
значение переданного ей аргумента будет равно единице. Если же при текущем вызове функции значение аргумента будет больше единицы, в блоке else условной
инструкции if будет предпринята попытка вычисления выражения n*recursive_func(n-1), использующее вызов функцией самой
себя. При этом прежде, чем вернуть конечный результат в точку вызова функции в программе, наша рекурсивная функция будет вызывать себя саму до тех пор, пока в выражении для возврата не
останется ее незавершенных вызовов. Например, для n=4, наш факториал схематически будет формироваться следующим образом:
return 4*recursive_func(4-1) -> return 4*(return 3*recursive_func(3-1)) ->
return 4*(return 3*(return 2*recursive_func(2-1))) ->
return 4*(return 3*(return 2*(return 1))) -> return 4*(return 3*(return 2*1)) ->
return 4*(return 3*2*1) -> return 4*3*2*1.
Следует заметить, что в языке Python рекурсии используются не так часто, как в некоторых других языках программирования, поскольку в
Python особое значение придается простым процедурным инструкциям вроде цикла while, который более естественно подходит
для решения аналогичных задач. Тем не менее рекурсии могут быть весьма полезными для реализации обхода сложных структур с произвольной организацией данных, поэтому взять их на вооружение
однозначно стоит.
Декораторы функций в Python
Декоратор (от англ. decorator) – это
функция, которая используется в качестве «обертки» другой функции с целью изменения ее поведения или расширения функциональности без непосредственного изменения кода самой функции.
Декораторы могут быть весьма полезны для расширения возможностей функций из сторонних библиотек, код которых мы не можем изменять, а также для изменения поведения или расширения
функциональности методов класса (о классах мы поговорим чуть позже). Все что нам нужно, это написать собственный или использовать уже готовый декоратор с требуемой дополнительной
функциональностью, передав ему объект целевой функции или метода для декорирования.
В качестве примера давайте создадим собственный простейший декоратор и на его основе познакомимся с этим понятием поближе (см. пример №17).
Код
Результат
pythonCodes
# Объект ф-ции передается декоратору в качестве аргумента.
def decorator_func(func):
# Внутри декораторы содержат функцию-обертку, которая
# изменяет поведение декорируемой функции func.
def wrapper_func(arg):
# Добавляем подсказку.
print('Начало выполнения функции!')
# Вызываем саму декорируемую функцию.
print(func(arg))
# Добавляем еще одну подсказку.
print('Выполнение функции закончено!')
# Возвращаем объект измененной func.
return wrapper_func
# Определим обычную польз. функцию.
def my_func(arg):
# Просто возвращаем аргумент.
return arg
# Получаем объект измененной функции my_func.
my_func_1 = decorator_func(my_func)
# Вызываем измененную функцию.
my_func_1('Выполняюсь!')
print()
# Декорируем встроенную функцию sum.
my_func_2 = decorator_func(sum)
# Вызываем измененную функцию.
my_func_2([1, 2, 3])
Начало выполнения функции!
Выполняюсь!
Выполнение функции закончено!
Начало выполнения функции!
6
Выполнение функции закончено!
Пример №17. Декораторы функций (часть 1).
Итак, в нашем примере мы создали декоратор, который добавляет декорируемым функциям сообщения о старте и финише их выполнения, а также выводит возвращаемые функциями
значения на экран. При этом мы не вносили никаких изменений в код декорируемых функций. Все изменения и дополнения произошли внутри тела декоратора, в котором мы определили
специальную функцию-обертку, содержащую дополнительный код и вызов исходной версии функции. Далее, использовав объект функции-обертки в качестве возвращаемого значения декоратора и
присвоив вызов декоратора переменной, мы получили готовый к использованию объект усовершенствованной версии переданной декоратору в качестве аргумента функции.
Таким образом, декораторы функций в общем случае принимают на вход исходную версию какой-либо функции или метода, а возвращают обертку переданной функции или метода, но уже с дополнительной
функциональностью. При этом в случаях, когда исходная версия функции становится больше не нужна, возвращаемую декоратором обертку присваивают переменной с именем исходной функции, тем самым
полностью заменяя ее.
Поскольку в Python декораторы используются довольно часто, для упрощения процедуры получения декорированных объектов используется специальный синтаксис,
предусматривающий использование имени декоратора с префиксом в виде символа @ непосредственно перед строкой, содержащей заголовок определения декорируемой
функции или метода (см. пример №18).
Код
Результат
pythonCodes
# Объект ф-ции передается декоратору в качестве аргумента.
def decorator_func(func):
# Внутри декораторы содержат функцию-обертку, которая
# изменяет поведение декорируемой функции func.
def wrapper_func(arg):
# Добавляем подсказку.
print('Начало выполнения функции!')
# Вызываем саму декорируемую функцию.
print(func(arg))
# Добавляем еще одну подсказку.
print('Выполнение функции закончено!')
# Возвращаем объект измененной func.
return wrapper_func
# Декорируем определяемую функцию.
@decorator_func
# Определим обычную польз. функцию.
def my_func(arg):
# Просто возвращаем аргумент.
return arg
# Вызываем измененную функцию.
my_func('Выполняюсь!')
Начало выполнения функции!
Выполняюсь!
Выполнение функции закончено!
Пример №18. Декораторы функций (часть 2).
Благодаря наличию такого синтаксиса нам не пришлось использовать в примере более длинную инструкцию my_func = decorator_func(my_func), что сделало код
более коротким и читабельным.
Атрибуты функций, аннотации и документирование в Python
Поскольку функции в Python являются обычными объектами, мы можем без каких-либо сложностей получать базовый доступ к предопределенным атрибутам функций,
а также добавлять, изменять или удалять собственные атрибуты (см. пример №19).
Код
Результат
pythonCodes
# Объявляем функцию и получаем объект.
def func(n): print(n*2)
# <function main.<locals>.func at 0x0000025137AA4670>.
print(func, end='\n\n')
# Выводим ее встроенные атрибуты.
print(dir(func))
# Выводим имя функции.
print(func.__name__)
# Выводим размер функции в байтах.
print(func.__sizeof__())
# Создаем свой атрибут в виде свойства.
func.count = 0
# Выводим значение атрибута.
print(func.count)
# Используем атрибут в вызове функции.
# Выведет 0.
func(func.count)
# Изменяем значение нашего атрибута.
func.count += 1
# Выведет 1.
print(func.count)
# Выведет 2.
func(func.count)
<function main.<locals>.func at 0x0000025137AA4670>
['__annotations__', '__builtins__', '__call__',...
func
128
0
0
1
2
Пример №19. Атрибуты функций в Python.
Как видим, для получения списка всех предопределенных атрибутов функции нужно просто воспользоваться встроенной функцией dir. Если же необходим доступ к
конкретному атрибуту, следует указать его имя в формате func.attr, где func – имя функции, а
attr – имя атрибута. Что касается пользовательских атрибутов, то они создаются и изменяются простым присваиванием требуемого значения имени атрибута,
который также записывается после имени функции через точку в формате func.attr. Собственные атрибуты позволяют эффективно и просто хранить информацию о
состоянии непосредственно в объекте функции, отказавшись от использования других приемов, таких как применение глобальных или нелокальных переменных и классов. Огромным
плюсом при этом является то, что в отличие от нелокальных переменных, атрибуты функции доступны в любом месте программы, где доступна сама функция. Так что в некотором смысле их можно
рассматривать, как имитацию статических локальных переменных, имеющихся в некоторых других языках программирования, т.к. они тоже сохраняют свои значения между вызовами функции.
Начиная с версии Python 3.0 у объектов функций появился новый встроенный атрибут __annotations__, который предназначен
для хранения краткого описания (аннотаций) аргументов функции и ее возвращаемого значения. Аннотации совершенно необязательны, но если они присутствуют, то их всегда можно получить через
этот атрибут, воспользовавшись синтаксической конструкцией func.__annotations__ (см. пример №20).
Код
Результат
pythonCodes
# Объявляем аннотированную функцию.
def func(name: 'имя', age: int) -> str:
'''Строка документирования: func.__doc__'''
# Составляем ник пользователя.
nick = name + '_' + str(age)
# Возвращаем его.
return nick
# Выведет Петр_35.
print(func('Петр', 35))
# Строка документирования: func.__doc__.
print(func.__doc__)
# {'name': 'имя', 'age': <class 'int'>, 'return': <class 'str'>}.
print(func.__annotations__)
Петр_35
Строка документирования: func.__doc__
{'name': 'имя', 'age': <class 'int'>, 'return': <class 'str'>}
Пример №20. Аннотации и документирование функций в Python.
Как видно из примера, синтаксически аннотации функции находятся в заголовке инструкции def в виде произвольных выражений, которые ассоциируются с ее
аргументами и возвращаемым значением. Для аргументов аннотации указываются через двоеточие сразу после имени аргумента, а для возвращаемого значения – после пары символов
-> вслед за списком аргументов. Все аннотации, если конечно они присутствуют в объявлении функции, интерпретатор собирает в словарь, который затем
присоединяет к объекту созданной функции. Имена аргументов в этом словаре становятся ключами, а сами аннотации их значениями. При этом для возвращаемого значения аннотация сохраняется под
ключом return.
Cтоит обратить внимание на еще один важный момент: в аннотированных аргументах, которые используют значения по умолчанию, аннотация записывается перед значением по умолчанию в
формате def func(arg_1: annot_1 = val_1, arg_2: annot_2 = val_2, …) -> return_annot:.
Что касается строки документирования, то она должна записываться в начале тела функции в тройных кавычках. Интерпретатор автоматически присоединит документацию к объекту функции и она станет
доступна в качестве значения его предопределенного атрибута __doc__.
Концепции проектирования функций в Python
Теперь, когда мы получили достаточно ясное представление о функциях в Python, хотелось бы подытожить тему несколькими полезными рекомендациями.
-
Каждая хорошо спроектированная функция должна иметь одно единственное назначение и решать только одну простую задачу. Например, если функция позволяет одновременно перемножать и
складывать числа, то наверняка стоит подумать над тем, чтобы разбить ее на две отдельные и более простые функции. -
Каждая функция должна иметь относительно небольшой размер. Если ваша функция начинает занимать несколько экранов – это явный признак того, что было бы неплохо разбить ее
на несколько более простых частей. Функции с большой глубиной вложенности часто свидетельствует о промахах в проектировании, тем более в языке Python,
которому присущи краткость и лаконичность. -
Используйте для передачи значений функции аргументы, а для возврата результатов – инструкцию return. Функция должна быть максимально независимой от
того, что происходит за ее пределами. -
Старайтесь использовать глобальные переменные внутри тела функции только в том случае, когда это действительно необходимо. Это поможет избежать зависимости от внешних факторов и проблем
с согласованностью компонентов программ, что, как следствие, существенно облегчит их отладку в дальнейшем. -
Следите за тем, чтобы ваши функции не воздействовали на изменяемые аргументы, если вызывающая программа не предполагает этого. Ведь получая в качестве аргументов, например, списки или
словари, функции могут оказывать воздействие на части этих изменяемых типов данных. В результате такие функции, опять же, становятся слишком тесно связаны и зависимы от внешней среды, что
может сделать их уж слишком специфичными и неустойчивыми. -
Избегайте непосредственного изменения переменных вашими функциями в других модулях программы. Используйте для этих целей отдельные функции доступа, сведя прямую зависимость к минимуму, как
и в случае с глобальными переменными.
Краткие итоги параграфа
- Функция в программировании – это блок программного кода на этом языке, который определяется один раз и далее может быть использован многократно.
-
Функции позволяют существенно уменьшить время и трудозатраты на разработку приложений, а также их дальнейшее сопровождение за счет следующих преимуществ: дают возможность разбить
сложную систему на небольшие и легко управляемые части, каждая из которых может разрабатываться отдельно, обеспечивают многократное использование программного кода, уменьшают его
избыточность, повышают читабельность исходного кода, упрощают его редактирование. -
Для создания функций в Python предназначена инструкция def, которая создает объект функции и связывает его с именем.
В общем виде инструкция имеет следующий формат:def <Имя функции>(arg_1, arg_1,... arg_n): <Тело функции> return <Возвращаемое значение>
Примером определения функции может служить инструкция def f(a, b): return a + b.
-
Инструкция return не является обязательной. При ее отсутствии функция будет завершать свою работу по достижении потоком управления конца тела функции.
При этом технически она все равно будет неявно возвращать результат в виде объекта None, однако он обычно просто игнорируется. -
Осуществляются вызовы функций только после их определения при помощи имени функции и круглых скобок с передаваемыми функции значениями аргументов, которые, если их несколько,
должны перечисляться через запятую. Например, для определенной выше функции вызов может иметь вид x = f(43, 85). -
В Python имеется целый ряд специальных режимов сопоставления аргументов, при использовании которых следует придерживаться следующих правил:
-
в заголовке определяемой функции аргументы должны указываться в следующем порядке: позиционные аргументы (arg_1, arg_2, …),
позиционные аргументы со значениями по умолчанию (arg_v_1=val_1, arg_v_2=val_2, …),
аргумент со звездочкой для сбора дополнительных позиционных аргументов в кортеж (*args), именованные аргументы
(named_arg_1, named_arg_2, …), именованные аргументы со значениями по умолчанию
(named_arg_1=val_1, named_arg_2=val_2, …) и аргумент с двумя звездочками для сбора дополнительных именованных аргументов в словарь
(**named_args); -
при вызове функции порядок передачи аргументов должен быть следующим: обычные позиционные аргументы (arg_1, arg_2, …), позиционные аргументы
в форме *args (будут распакованы интерпретатором в значения arg_1, arg_2, …), обычные именованные аргументы
(named_arg_1=val_1, named_arg_2=val_2, …) и в самом конце именованные аргументы в форме **named_args
(будут распакованы интерпретатором в пары named_arg_1=val_1, named_arg_2=val_2, …).
-
в заголовке определяемой функции аргументы должны указываться в следующем порядке: позиционные аргументы (arg_1, arg_2, …),
-
В пределах модуля имена могут находиться в трех основных областях видимости, которые определяются местом инициализации переменных.
-
Если присваивание переменной выполняется внутри инструкции def, переменная становится локальной для этой функции. Это касается как аргументов
функции, так и переменных, созданных в теле функции. Локальные переменные доступны только внутри своей функции, т.е. в текущей локальной области видимости, и недоступны за ее
пределами, включая другие локальные области видимости невложенных в нее функций. -
Если присваивание производится в пределах объемлющей инструкции def, переменная становится локальной для данной объемлющей функции и нелокальной
для данной вложенной функции. В общем случае такая переменная будет доступна внутри любой вложенной функции при отсутствии в ней локальной переменной с таким же именем. -
Если присваивание производится на верхнем уровне модуля (файла скрипта) за пределами всех инструкций def, переменная становится глобальной для
всего файла. В общем случае глобальная переменная будет доступна внутри любой функции текущего модуля при отсутствии в ней локальной переменной с таким же именем.
-
Если присваивание переменной выполняется внутри инструкции def, переменная становится локальной для этой функции. Это касается как аргументов
-
Получить информацию о глобальных и локальных переменных известных интерпретатору на данный момент можно при помощи встроенных функций globals() и
locals(), которые возвращают словари со значениями соответствующих переменных, имена которых используются в словарях в качестве ключей. -
Поиск имен интерпретатором осуществляется следующим образом: когда внутри функции выполняется обращение к неизвестному имени, интерпретатор пытается сначала отыскать его в локальной
области видимости, затем в локальной области любой объемлющей инструкции def или в выражении lambda и далее в
глобальной области видимости. Как только первое подходящее имя будет найдено, поиск завершается. Если искомого имени обнаружено не будет, интерпретатор выведет сообщение об ошибке. -
Для создания простых функций непосредственно внутри выражений в Python используются lambda-выражения, которые еще
называют анонимными или безымянными функциями, т.к. они хоть и создают функции, но не связывают их с именами. При этом важно помнить, что в теле анонимной функции может содержаться
только одно выражение и никаких инструкций. -
В Python допускается использование рекурсивных функций, которые позволяют осуществлять вызов самих себя до выполнения особого условия,
останавливающего дальнейшие рекурсии. Используются рекурсивные функции не так часто, как в некоторых других языках программирования, зато они могут оказаться весьма полезными
для реализации обхода структур с произвольной организацией данных. -
Также для Python характерно использование декораторов, т.е. специальных функций-оберток, которые позволяют изменять поведение и расширять
функциональность передаваемых им в качестве аргументов других функций и методов, что бывает весьма полезным для расширения возможностей функций из сторонних библиотек, код которых
мы не можем изменять, а также для изменения поведения или расширения функциональности методов классов. -
Поскольку функции в Python являются обычными объектами, у них имеются предопределенные атрибуты, а также могут добавляться, изменяться или удаляться
пользовательские атрибуты. В общем случае доступ к атрибутам можно получить при помощи имени атрибута, указанного через точку после имени функции. -
Начиная с версии Python 3.0 у объектов функций появился новый встроенный атрибут __annotations__, используемый
для хранения аннотаций к аргументам функции и возвращаемому значению. В общем виде синтаксис записи аннотаций выглядит так:
def func(arg_1: annot_1, … arg_k: annot_k = val_k, …) -> return_annot:. -
Помимо аннотаций в функции разрешается добавлять строки документирования. Такая строка должна записываться в начале тела функции в тройных кавычках. Интерпретатор
автоматически присоединит ее к объекту функции и далее созданная документация станет доступна в качестве значения его предопределенного атрибута
__doc__. -
В ходе проектирования функций следует придерживаться следующих основных правил: функция должна быть максимально короткой и выполнять одну простую задачу; значения в функцию должны
передаваться через аргументы, а результат возвращаться через инструкцию return; глобальные переменные должны использоваться внутри функции только
в том случае, когда это действительно необходимо; функции не должны воздействовать на изменяемые аргументы, если вызывающая программа не предполагает этого; изменение переменных в
других модулях следует осуществлять через специально созданные для этого функции доступа, избегая непосредственного их изменения.
Вопросы и задания для самоконтроля
1. Дайте определение функции в программировании. Перечислите основные преимущества использования функций.
Показать решение.
Ответ. Функция в программировании – это блок программного кода на этом языке, который определяется один
раз и далее может быть использован многократно.
Функции позволяют существенно уменьшить время и трудозатраты на разработку приложений, а также их дальнейшее сопровождение за счет следующих преимуществ:
- дают возможность разбить сложную систему на небольшие и легко управляемые части, каждая из которых может разрабатываться отдельно;
- обеспечивают многократное использование программного кода;
- уменьшают его избыточность;
- повышают читабельность исходного кода;
- упрощают его редактирование.
2. В какой момент времени интерпретатор Python создает функции? Разрешается ли вызывать функции до момента их определения?
Показать решение.
Ответ. Функция создается, когда поток выполнения программы достигает инструкции def и выполняет ее. Эта
инструкция создает объект функции и связывает его с именем функции. С этого момента функция становится доступной для вызовов: для этого нужно к имени функции добавить круглые скобки со
значениями аргументов внутри. Вызывать функцию до момента ее определения в Python запрещается. Стоит заметить, что в некоторых других языках программирования
такое возможно за счет того, что определения функций поднимаются вверх скриптов. Но в Python вызывать функцию до момента ее определения запрещается!
3. Когда выполняется непосредственно сам программный код, вложенный в тело функции?
Показать решение.
Ответ. Тело функции выполняется каждый раз при вызове уже определенной функции.
4. Что возвращает функция, в которой отсутствует инструкция return?
Показать решение.
Ответ. Инструкция return не является обязательной. При ее отсутствии функция будет завершать свою работу по
достижении потоком управления конца тела функции. При этом технически она все равно будет неявно возвращать результат в виде объекта None, однако он обычно
просто игнорируется.
5. Какой из представленных вариантов определения функции не содержит ошибок: dfn f(x, y): return x*y,
def f(x, y): return x*y или def f{x, y}: return x*y.
Показать решение.
Ответ. def f(x, y): return x*y
6. Каковы будут результаты вызовов функций в коде условия? Объясните свои ответы, а затем запустите скрипт и проверьте себя.
Показать решение.
Условие
pythonCodes
# 1.
def func_1(a, b=2, c=3): print(a, b, c)
func_1(1, 2)
# 2.
def func_2(a, b, c=3): print(a, b, c)
func_2(1, c=4, b=5)
# 3.
def func_3(a, *p_args): print(a, p_args)
func_3(1, 2, 3)
# 4.
def func_4(a, b, c=3): print(a, b, c)
func_4(2, *(4, 5))
# 5.
def func_5(a, **n_args): print(a, n_args)
func_5(1, c=3, b=2)
# 6.
def func_6(a, *, b, **n_args): print(a, b, n_args)
func_6(1, c=3, b=2)
Решение
Результат
pythonCodes
# Два аргумента передали по умолчанию.
def func_1(a, b=2, c=3): print(a, b, c)
# Выведет 1 2 3, т.к. позиционные
# аргументы сопоставляются по позициям.
func_1(1, 2)
# По умолчанию передали только 1 арг-т.
def func_2(a, b, c=3): print(a, b, c)
# Выведет 1 5 4, т.к. передаем арг-ты
# по именам хоть и не в том порядке.
func_2(1, c=4, b=5)
# Доп. поз-ные арг-ты собираем в кортеж.
def func_3(a, *p_args): print(a, p_args)
# Выведет 1 (2, 3), т.к. два дополнительных
# аргумента добавились в кортеж.
func_3(1, 2, 3)
# Имеем 1 позиционный арг-т по умолчанию.
def func_4(a, b, c=3): print(a, b, c)
# Выведет 2 4 5, т.к. кортеж распаковался
# в позиционные значения 4 и 5.
func_4(2, *(4, 5))
# Доп. именованные арг-ты собираем в словарь.
def func_5(a, **n_args): print(a, n_args)
# Выведет 1 {'c': 3, 'b': 2}, т.к. было передано
# 2 именованных аргумента хоть и не в том порядке.
func_5(1, c=3, b=2)
# После * определяем именованные аргументы.
def func_6(a, *, b, **n_args): print(a, b, n_args)
# 1 2 {'c': 3}, т.к. а - поз-ный арг-т, b - имен-ный,
# с - доп-ный именованный (он и добавился в словарь).
func_6(1, c=3, b=2)
1 2 3
1 5 4
1 (2, 3)
2 4 5
1 {'c': 3, 'b': 2}
1 2 {'c': 3}
7. Какие переменные в Python называются глобальными, а какие локальными? Доступны ли глобальные переменные внутри
функций, а локальные на верхнем уровне модуля?
Показать решение.
Ответ. Переменные объявленные за пределами всех инструкций def на верхнем уровне модуля (файла скрипта),
называются глобальными. В общем случае глобальная переменная будет доступна внутри любой функции текущего модуля при отсутствии в ней локальной переменной с таким же именем. Переменные
объявленные внутри функции называются ее локальными переменными. Локальные переменные доступны только внутри своей функции, т.е. в текущей локальной области видимости, и недоступны за ее
пределами, включая другие локальные области видимости невложенных в нее функций.
8. Что будет выведено на экран фрагментами кода условия? Объясните свои ответы, а затем запустите скрипт и проверьте себя.
Показать решение.
Условие
pythonCodes
# 1.
x_1 = 1
def func_1(): print(x_1)
func_1()
# 2.
x_2 = 2
def func_2(): x_2 = 22
func_2()
print(x_2)
# 3.
x_3 = 3
def func_3():
x_3 = 33
print(x_3)
func_3()
print(x_3)
# 4.
x_4 = 4
def func_4():
global x_4
x_4 = 44
func_4()
print(x_4)
# 5.
x_5 = 5
def func_5():
x_5 = 55
def nested_5(): print(x_5)
nested_5()
func_5()
print(x_5)
# 6.
x_6 = 6
def func_6():
x_6 = 66
print(x_6)
def nested_6():
nonlocal x_6
x_6 = 666
nested_6()
print(x_6)
func_6()
print(x_6)
Решение
Результат
pythonCodes
# Глобальная переменная.
x_1 = 1
# Глоб. пер-е доступны везде.
def func_1(): print(x_1)
# Выведет 1.
func_1()
# Глобальная переменная.
x_2 = 2
# Лок-ная имеет приоритет внутри ф-ции.
def func_2(): x_2 = 22
# Ничего не выводит.
func_2()
# Выведет значение глоб. пер-ной, т.е. 2.
print(x_2)
# Глобальная переменная.
x_3 = 3
def func_3():
# Локальная переменная.
x_3 = 33
# Лок-ная имеет приоритет внутри ф-ции.
print(x_3)
# Выведет значение лок. пер-ной, т.е. 33.
func_3()
# Вне ф-ции лок. пер-ная недоступна.
# Выведет значение глоб. пер-ной, т.е. 3.
print(x_3)
# Глобальная переменная.
x_4 = 4
# Выведет новое значение глоб. пер-ной, т.е. 44.
print(x_4)
def func_4():
# Использ. глоб. пер-ную.
global x_4
# Значение присваивается глоб. пер-ной.
x_4 = 44
# Вызов изменяет значение глоб. пер-ной.
func_4()
# В глоб. области лок-ные пер-ные недоступны.
# Выведет новое значение глоб. пер-ной, т.е. 44.
print(x_4)
# Глобальная переменная.
x_5 = 5
def func_5():
# Локальная переменная для func_5.
x_5 = 55
# Локальные переменные объемлющей ф-ции
# доступны во всех вложенных функциях.
def nested_5(): print(x_5)
# Выведет 55 при вызове func_5().
nested_5()
# Выведет 55.
func_5()
# В глоб. области лок-ные пер-ные недоступны.
# Выведет значение глоб. пер-ной, т.е. 5.
print(x_5)
# Глобальная переменная.
x_6 = 6
def func_6():
# Локальная переменная для func_6.
# Нелокальная переменная для nested_6.
x_6 = 66
# Выведет 66 при вызове func_6().
print(x_6)
def nested_6():
# Использ. нелок-ную. пер-ную.
nonlocal x_6
# Присваиваем значение нелок-ной пер-ной.
x_6 = 666
# Вызов изменяет нелок-ную пер-ную.
nested_6()
# Выведет 666 при вызове func_6().
print(x_6)
# Выведет 666.
func_6()
# В глоб. области лок-ные пер-ные недоступны.
# Выведет значение глоб. пер-ной, т.е. 6.
print(x_6)
1
2
33
3
4
44
55
5
66
666
6
9. Сосчитайте локальные переменные функции func(x). Сколько локальных переменных будет отображено в словаре
loc_vars при первом и втором вызовах функции locals() и почему?
Показать решение.
Условие
pythonCodes
def func(x):
y = 5
z = 10
loc_vars = locals()
sum = x + y + z
loc_vars = locals()
return sum
print(func(20))
Ответ. Всего в локальной области видимости функции func(x) имеется пять локальных переменных. Все они
будут перечислены в словаре, который возвращает второй вызов функции locals(). Однако после первого вызова функции в переменной
loc_vars будет содержаться словарь лишь с тремя локальными переменными x, y и
z, т.к. только они будут известны интерпретатору на момент вызова функции. Сама локальная переменная loc_vars становится
известной интерпретатору уже после вызова функции locals() в момент присваивания ей значения (инструкция присваивания имеет правую ассоциативность).
Решение
Результат
pythonCodes
# Объявляем функцию.
def func(x):
# Объявляем локальные переменные.
y = 5
z = 10
# Получаем словарь доступных локальных переменных.
loc_vars = locals()
print('loc_vars:', loc_vars)
# Объявляем еще одну локальную переменную.
sum = x + y + z
# Обновляем словарь локальных переменных для func_1.
loc_vars = locals()
print('loc_vars:', loc_vars)
return sum
# Вызываем функцию.
print(func(20))
loc_vars: {'x': 20, 'y': 5, 'z': 10}
loc_vars: {'x': 20, 'y': 5, 'z': 10, 'loc_vars': {...}, 'sum': 35}
35
10. Перечислите основные отличия lambda-выражения от инструкции def.
Показать решение.
Ответ. В отличие от обычной инструкции объявления функции def лямбда-выражение обладает следующими
особенностями: создает объект функции, но не связывает его с именем; является выражением и может использоваться внутри выражений; внутри тела может содержать только одно выражение и
никаких инструкций.
11. Что представляют из себя рекурсивные функции?
Показать решение.
Ответ. Рекурсивные функции – это функции, которые содержат код вызова самой
себя в целях организации циклического процесса. Во всем остальном они представляют собой вполне обычные функции со всеми присущими им особенностями.
12. Каков синтаксис использования одной функции в качестве декоратора другой?
Показать решение.
Ответ. Необходимо написать в строке, предшествующей строке с заголовком определяемой декорируемой функции, имя декоратора с префиксом в виде
символа @.
Решение
Результат
pythonCodes
# Это наш декоратор.
def decorator_func(func):
# Внутри декораторы содержат функцию-обертку, которая
# изменяет поведение декорируемой функции func.
def wrapper_func(arg):
# Добавляем подсказку.
print('Начало выполнения функции!')
# Вызываем саму декорируемую функцию.
print(func(arg))
# Добавляем еще одну подсказку.
print('Выполнение функции закончено!')
# Возвращаем объект измененной func.
return wrapper_func
# Декорируем определяемую функцию.
@decorator_func
# Определим обычную польз. функцию.
def my_func(arg):
# Просто возвращаем аргумент.
return arg
# Вызываем измененную функцию.
my_func('Выполняюсь!')
Начало выполнения функции!
Выполняюсь!
Выполнение функции закончено!
13. Каких наиболее общих рекомендаций следует придерживаться в ходе проектирования функций?
Показать решение.
Ответ. В ходе проектирования функций следует придерживаться следующих основных правил:
функция должна быть максимально короткой и выполнять одну простую задачу;
значения в функцию должны передаваться через аргументы, а результат возвращаться через инструкцию return;
глобальные переменные должны использоваться внутри функции только в том случае, когда это действительно необходимо;
функции не должны воздействовать на изменяемые аргументы, если вызывающая программа не предполагает этого;
изменение переменных в других модулях следует осуществлять через специально созданные для этого функции доступа, избегая непосредственного их изменения.
14. Дополнительные тесты по теме расположены в разделе
«Функции» нашего сборника тестов по основам
Пайтона.
15. Дополнительные упражнения и задачи по теме расположены в разделе
«Функции»
нашего сборника задач и упражнений.
Быстрый переход к другим страницам
Мы зачастую хотим делить нашу программу на составные блоки, это могут быть модули/пакеты модулей
, классы
для написания программ в парадигме ООП
. Одним из составных блоков нашей программы может быть функция.
Если говорить просто, то функция – это средство, позволяющее группировать наборы инструкций так, что в программе они могут запускаться неоднократно. Функции могут вычислять некоторый результат и позволять указывать вход- ные параметры, отличающиеся по своим значениям от вызова к вызову. Воз- можность оформления операций в виде функций – это очень удобный инстру- мент, который мы можем использовать в самых разных ситуациях.
Для объявления функций используется инструкция def
. Пример простой функции с именем foo
, которая ничего не делает
Функции получают аргументы. Аргументы мы перечисляем в круглых скобочках.
Вызов функций
Для вызова функции(т.е перехода к исполнению ее инструкций) используется оператор вызова функции:
Возврат значения из функции / оператор return
Наши функции делают какую-то работу, мы это работу хотим из функции получить, для того чтобы вернуть значение из функции используется оператор return
def getValue():
return «value»
print(getValue())
Если мы явно ничего из функции не возвращали — из функции вернется None
def bar():
pass
print(bar())
Помимо этого инструкция return
прекращает дальнейшее исполнение функции и мы можем исползовать return
без указания возвращаемого значения:
def baz(a):
if a > 10:
return
return «‘a’ less then 10»
print(baz(5))
print(baz(11))
Аргументы функции
- Позиционные аргументы — аргументы передаваемые при вызове по их порядку в объявлении функции
- Именованные аргументы — аргументы передаваемые по их имени, указанному при объвлении функции
def bar(arg1, arg2):
print(f»arg1: {arg1}, arg2: {arg2}«)
bar(1, arg2=2)
Аргументы со значением по умолчанию (Опициональные)
Если вы объявили функцию с 2 аргументами и не указали значения по умолчанию
— эти аргументы будут обязательными при вызове функции, для объявления функции с опциональными аргументами
используется следующий синтаксис:
def foo(arg1 = «default value»):
print(f»arg1 = {arg1}«)
foo()
В примере мы создали функцию foo
, которая получает один аргумент, но он не является обязательным, потому что для него указано значение по умолчанию(«default value»)
❗️Обратите внимание
Значения по умолчанию инициализируются во время инициализации функции, в других языках, которые я знаю значение по умолчанию инициализируется при каждом вызове функции, это может приводить к неблагоприятным сайд-эффектам.
Для большей ясности я продемонстрирую вам пример:def addvalue(value, target_list= []):
«»»
Функция добавляющая значение в лист и возвращающая его, зачем она это делает?.. не будем углубляться
Но интересный факт, что если лист не передан вторым аргументом по умолчанию будет использоваться значение по умолчанию
«»»
target_list.append(value)
return target_list
print(addvalue(1))
print(addvalue(2))
Живой примерчик:
Сворачивание аргументов
Нам было бы не удобно если функции могли получать только ограниченное кол-во аргументов, поэтому мы можем делать сворачивание аргументов. Для начала рассмотрим пример функции sum
которая получает только 2 аргумента и возвращает их сумму:
def sum(a, b):
return a + b
Но что делать если мы хотим вызывать функцию с несколькими аргументами?
В таком случае давайте сделаем так, чтобы все позиционные аргументы собирались в какую-то одну переменную-аргумент
, назовем ее args
. Для сворачивания аргуметов используется символ *
def sum(*args):
result = 0
print(args)
for i in args:
if type(i) == int:
result += i
return result
sum(1, 2, 3)
Вполне естественно для понимания, что переменная args
внутри функции будет типа tuple
. Примерно такое же вы можете делать и для именованных аргументов
def sum(*args, limit=None, **kwargs):
result = 0
print(args)
print(kwargs)
for i in args:
if type(i) == int:
result += i
return result
sum(1, 2, 3, limit=20, offset=10)
Для того чтобы свернуть все оставшиеся именованные аргументы используется синтаксис с двумя звездочками **
. А тип переменной kwargs
будет dict
.
Только позиционные и только именованные аргументы
⚠️Важно заметить
Только позиционные
аргументы появились вPython 3.8
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
———— ———- ———-
| | |
| Positional or keyword |
| — Keyword only
— Positional only
Только именованные
Для того чтобы сделать аргументы только именованными
нам надо просто воспользоваться сворачиванием позиционных аргументов
через *
или *args
. Все перечисленные аргументы полсле сворачивания будут передаваться только по имени.
Только позиционные
Для того чтобы сделать аргументы только позиционными
они отделяются символом /
. (Как показано в примере выше)
Что делает def
?
Давайте теперь рассмотрим что делает оператор def
, и для того чтобы меньше самому писать я обращусь к Лутцу.
-
def
– это исполняемый программный код. Функции в языкеPython
создаются с помощью новой инструкцииdef
. В отличие от функций в компилирующих языках программирования, таких какC
,def
относится к классу исполняемых инструкций – функция не существует, пока интерпретатор не доберется до инструкции def и не выполнит ее. Фактически вполне допу- стимо (а иногда даже полезно) вкладывать инструкцииdef
внутрь инструкцийif
, цикловwhile
и даже в другие инструкцииdef
. В случае наиболее типичного использования инструкцииdef
вставляются в файлы модулей и генерируют функции при выполнении во время первой операции импор- тирования. -
def
создает объект и присваивает ему имя. Когда интерпретаторPython
встречает и выполняет инструкциюdef
, он создает новыйобъект-функцию
и связывает его с именем функции. Как и в любой другой операции при- сваивания, имя становится ссылкой на объект-функцию. В имени функции нет ничего необычного – как будет показано далее, объект-функция может быть связан с несколькими именами, может сохраняться в списке и так да- лее. Кроме того, к функциям можно прикреплять различные атрибуты, определяемые пользователем, для сохранения каких-либо данных.
Что мы можем понять из этих двух пунктов? А то, что когда вы используете инструкцию def
вы просто создаете объект, у которого есть имя и магический метод __call__
. По этим причинам следующий код будет работать:
def foo():
return «I am foo»
print(foo.__call__())
print(foo.__name__)
setattr(foo, «name», «value»)
print(foo.name)
foo.__call__.__call__.__call__()
Рекурсивный вызов функций
Давайте так.. рекурсивный вызов
или рекурсия
— это когда функция может вызывать саму себя.. может мы потом подробнее поговорим про стек вызовов, но это совершенно естественно в программировании.
Вот пример получения n`ого-числа Фибоначи:
def fib(n):
if n < 2:
return n
return fib(n—1) + fib(n—2)
print(fib(8))
❗️Обратите внимание
С помощью рекурсии очень удобно обрабатывать древовидные структуры(
HTML
,Дерево директорий
…)
Что такое функция в Python
На языке разработчиков функция — это блок программного кода, который можно использовать несколько раз для решения разных задач. Тело функции — набор инструкций, которые поочередно исполняют в процессе вызова.
Функцию можно сравнить с кофемашиной — она принимает кофе и молоко (аргументы), а возвращает латте, эспрессо, американо и другие виды кофе.
Синтаксис функций в Python
Любые функции и методы начинаются с инструкции def (от английского define — «определять»). За def в Python идет название функции, после — набор аргументов. Их указывают в скобках. Двоеточие в конце открывает блок тела функции, которое располагают с новой строки.
Изучить все базовые функции и методы Python можно на курсе Skypro «Python-разработчик». За несколько месяцев вы освоите нужную теорию и навыки, чтобы найти работу по новой специальности. А специалисты центра карьеры помогут составить резюме и подготовиться к собеседованию.
В Python нет фигурных скобок для блока кода. Вместо них используют отступы в четыре пробела. Новая инструкция — новая строка. Еще их можно отделить точкой с запятой.
Чтобы завершить функцию и вернуть из нее значение, используют ключевое слово return. Если этого не указать, то, когда тело функции достигнет конца, функция завершится и вернет объект типа None.
К примеру, сейчас функция sayHello ничего не возвращает, а просто выводит текст в консоль. Если хотите, чтобы она возвращала что-то, измените ее код:
Теперь функция возвращает строку, а значение можно сохранять в переменной и использовать.
Подробнее об этом расскажет наш спикер на видео:
< видео >
Как вызвать функцию в Python
Нужно написать имя функции, а затем в круглых скобках указать необходимые аргументы, чтобы вызвать функцию Python. Например:
sayHello(name)
Однако с результатом функции после этой операции ничего сделать не получится. Поэтому лучше сразу поместить результат в переменную:
result = say_hello("name")
print(result)
Результатом такой операции будет строка Hello, name!, которую затем можно использовать где-то еще.
Точно так же можно проводить в функции и арифметические операции:
def addfunc(val1, val2):
return val1 + val2
Эта функция принимает два значения (val1, val2) и возвращает их сумму с помощью return val1 + val2. Например:
result = addfunc(3, 5)
print(result) # Выведет: 8
Аргументы и их виды
Чтобы передать в функцию данные, укажите их в скобках после имени функции.
Обычные аргументы передаются строго по порядку. Но есть и именованные — их можно передавать в любом порядке, главное — правильно указывать имена.
Позиционные
Это — обязательные аргументы. Их надо передавать функции в определенном порядке.
def plus(a, b):
return a + b
result = сложить(2, 3)
print(результат) # 5
Если не передать нужное количество аргументов, Python выдаст ошибку TypeError.
Если нужно передать не два аргумента, а больше, используйте *args. Все значения соберутся в кортеж — тип данных, в котором хранят упорядоченную последовательность элементов.
def sum(*числа):
total = 0
for number in numbers:
if isinstance(number, (int, float)):
total += number
else:
print(f"{number} — не число")
return total
итог = сумма(1, 2, "Привет", 3.5, 4)
print("Сумма:", итог) # Сумма: 10.5
Аргументы из *args можно перебирать, а можно обращаться к ним по индексу.
def showArgs(*args):
if len(args) > 0:
print("Первый:", args[0])
if len(args) > 1:
print("Второй:", args[1])
showArgs("один", "два")
Именованные
Именованными называют аргументы, у которых есть значение по умолчанию. Их можно не указывать при вызове, но если нужно, можно переопределить по имени.
def greet(имя="Гость"):
print("Привет,", имя)
greet() # Привет, Гость
greet(имя="Аня") # Привет, Аня
Чтобы передать функции много именованных аргументов, используют **kwargs. Такие аргументы собираются в словарь: специальную структуру данных из пар «ключ-значение».
def showData(**данные):
print(данные)
showData(имя="Кира", возраст=25, город="Минск")
# {'имя': 'Кира', 'возраст': 25, 'город': 'Минск'}
Функции и области видимости
Область видимости нужна, чтобы не было конфликтов имен. Объекты, которые были созданы внутри функции, не перезаписывают внешние переменные с тем же именем.
Есть три основные области видимости: глобальная, локальная и область объемлющей функции.
Глобальная область (global scope)
Это пространство имен всего файла (модуля). В него входят переменные, функции и т. п., объявленные вне других блоков.
x = 10
def showVar():
print(x)
showVar() # 10
Глобальные переменные видны из любого места в файле или модуле. Их нельзя изменить без ключевого слова Global.
Локальная область (local scope)
Внутри функций можно создавать временные переменные для вычислений. Чтобы избежать конфликта имен и случайной перезаписи, под каждую функцию выделяется свое пространство:
x = 100 # глобальная переменная
def first():
print(x) # использует глобальную переменную
def second():
x = "local" # создает локальную переменную
print(x)
def third():
global x # указывает, что будет изменяться глобальная переменная
x = "modified globally"
first() # 100
second() # local
third()
first() # modified globally
Локальные переменные живут только внутри функции и исчезают после ее завершения. Если в разных функциях создать переменные с одинаковыми именами, они не мешают друг другу — у каждой функции будет своя копия.
Область объемлющей функции (enclosing function scope)
С помощью nonlocal можно использовать переменные из внешней (но не глобальной) функции. Это часто нужно в декораторах — функциях, которые добавляют к другой функции новое поведение, но при этом не изменяют ее код.
def counter(func):
count = 0 # переменная во внешней функции
def wrapper():
nonlocal count # указываем, что используем переменную из внешней области
func()
count += 1
print("Calls:", count)
return wrapper
@counter
def demo():
print("Message")
for _ in range(3):
demo()
Эта область возникает, когда функция находится внутри другой функции — внутренняя функция видит переменные внешней.
Ключевое слово nonlocal нужно, чтобы менять переменные внешней (объемлющей) функции. Без него вместо изменения в существующую переменную получится новая локальная переменная.
Lambda-функции (анонимные)
Lambda-функции — это короткие однострочные функции. Их удобно использовать в простых операциях, например фильтрации списков.
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers)) # фильтруем четные числа
print(evens) # [2, 4, 6]
Лямбда-функции в Python — анонимные: у них нет имени.
Рекурсивные функции в Python
Рекурсия — это прием, когда функция вызывает саму себя. Это удобно для задач, где решение можно разбить на более простые, похожие подзадачи. Например, когда надо вычислить факториал числа или обход дерева.
def factorial(n):
if n == 0:
return 1 # базовый случай: факториал 0 равен 1
return n * factorial(n - 1) # рекурсивный вызов
print(factorial(5)) # 120
Важно: у каждой рекурсивной функции должен быть базовый случай — условие, при котором она завершает вызовы и не вызывает себя дальше. Иначе можно создать бесконечную рекурсию и ошибку RecursionError.
Декораторы функций в Python
Декоратор может добавлять поведение до или после вызова функции, при этом он не будет трогать ее исходный код. Часто используется, чтобы считать вызовы, логировать, проверять права доступа и т. д.
Пример:
def log_decorator(func):
def wrapper():
print("Вызов функции:", func.__name__)
func()
print("Завершение работы")
return wrapper
@log_decorator
def say_hello():
print("Hello!")
say_hello()
Функция возвращает такой результат:
Вызов функции: say_hello
Hello!
Завершение работы
Символ @ перед именем декоратора — это синтаксический сахар: он не влияет на поведение программы, но с ним код проще читать и воспринимать.
Оператор возврата return
return возвращает результат из функции. Можно вернуть несколько значений — они будут собраны в кортеж. Такие значения можно потом распаковать в переменные.
def calculate(a, b):
return a + b, a - b, a * b, a / b
results = calculate(10, 2)
print(results) # (12, 8, 20, 5.0)
print(results[0]) # 12
s, d, m, q = calculate(4, 2)
print(s) # 6
print(m) # 8
Оператор возврата yield
Разработчики используют yield в генераторах — функциях, которые возвращают значения по одному. Такие функции экономят память и выполняют отложенные вычисления.
def generator(n, text):
for _ in range(n):
yield text # возвращает значение и запоминает позицию
g = generator(3, "Example")
for item in g:
print(item)
После того как генератор закончится, при следующем вызове next() возникнет ошибка StopIteration.
[FAQ] Частые вопросы
Функции vs процедуры — в чём отличие?
Процедура — это единичная инструкция, функция — подпрограмма, которая выполняет действие.
Можно ли объявить функцию внутри другой функции?
Да. Такие функции называются вложенными. Они могут использовать переменные объемлющей функции и часто применяются для создания замыканий.
Можно ли использовать return и yield в одной функции?
Нет. Функция использует либо return, либо yield. Если есть хотя бы один yield, функция становится генератором.
Что произойдет, если вызвать функцию до ее определения?
Python выдаст ошибку NameError. Функции нужно определять до их вызова в коде.
Можно ли присвоить функцию переменной?[/txt]
Да, в Python функции — это объекты. Их можно присваивать переменным, передавать как аргументы и возвращать из других функций.
Для чего нужна область видимости?
Чтобы решить конфликт имен.
Как провести распаковку множества?
Нужно присвоить его такому же количеству переменных, сколько в нём содержится объектов.
Обязательно ли наличие аргументов для функции?
Нет, но чаще у функции есть один или несколько аргументов.
Можно ли передавать в функцию другие функции?
Да. Так разработчики могут реализовывать функции высшего порядка и создавать декораторы, фильтры и т. д.
Если эта статья показалась вам слишком сложной, возможно, вы пропустили какие-то основы Python. Изучить их можно на курсе Skypro «Python-разработчик». Вы начнете освоение языка с нуля, изучите базу применения переменных, кортежей и словарей, методов и функций и перейдете к продвинутым навыкам: работе с обертками, декораторами и т. п.
Главное о функциях в Python
- Функция — это блок кода, который можно вызывать много раз. Она помогает избежать повторения одного и того же.
- Функции создают с помощью def, после пишут имя и аргументы в скобках. Тело функции оформляют с отступом.
return завершает выполнение функции и возвращает результат. Без него функция вернет None. - Чтобы знать, как вызвать функцию, достаточно знать ее имя и передать ей аргументы в скобках. Результат можно сохранить в переменной.
- Аргументы могут быть позиционными (по порядку) и именованными (с указанием имени). Есть специальные — *args (много значений) и **kwargs (много пар «ключ-значение»).
- Переменные внутри функции не видны снаружи. Есть глобальная, локальная и область объемлющей функции. Для доступа к внешним переменным используют global и nonlocal.
- Это короткие функции без имени. Удобны для простых операций, например при фильтрации.
- Это функции, которые вызывают сами себя. Обязательно нужно указывать базовый случай, чтобы остановить рекурсию.
- Декораторы оборачивают другие функции и добавляют поведение. Используются, например, для логирования или подсчета вызовов.
return возвращает сразу результат. yield — по одному значению за раз, работает в генераторах. В одной функции нельзя использовать и то и другое. - Функции можно создавать внутри других. Их можно присваивать переменным и передавать как аргументы.
- Перед вызовом функцию нужно определить.
Функции — это важный инструмент для упрощения и организации кода. Они позволяют выполнять повторяющиеся задачи, инкапсулировать логику и делать код более читаемым. В Python функции определяются с помощью ключевого слова def
. В этой лекции мы подробно рассмотрим, как определить функции, какие они могут иметь особенности и приведём множество примеров их использования.
Что такое функция?
Функция — это именованный блок кода, который выполняет определённую задачу. После определения функции её можно вызвать несколько раз в различных частях программы. Это позволяет избежать повторения кода, улучшает его структуру и упрощает тестирование и отладку.
Определение функции
Чтобы определить функцию в Python, используйте ключевое слово def
, затем укажите имя функции, за которым следуют круглые скобки (для аргументов) и двоеточие. После этого пишите тело функции, которое должно быть отступлено на один уровень.
Общая структура функции:
def имя_функции(аргументы):
# тело функции
Пример 1: Простая функция без аргументов
Рассмотрим простую функцию, которая выводит сообщение на экран:
def say_hello():
print("Привет, мир!")
Эта функция не требует аргументов и просто выполняет печать сообщения. Вызов функции осуществляется так:
say_hello() # Выведет: Привет, мир!
Пример 2: Функция с аргументами
Функции могут принимать аргументы, которые позволяют передавать данные в функцию. Пример функции, принимающей один аргумент:
def greet(name):
print(f"Привет, {name}!")
Теперь можно передать имя пользователя в функцию:
greet("Мария") # Выведет: Привет, Мария!
Пример 3: Функция с несколькими аргументами
Функции могут принимать несколько аргументов. Рассмотрим функцию, которая складывает два числа:
def add(a, b):
return a + b
Функция возвращает сумму двух чисел:
result = add(5, 7)
print(result) # Выведет: 12
Пример 4: Возвращаемое значение
Функции могут возвращать значения, которые могут быть использованы в дальнейшем в программе. Например, функция для вычисления произведения двух чисел:
def multiply(x, y):
return x * y
Результат функции можно сохранить в переменную и использовать её далее:
product = multiply(4, 6)
print(product) # Выведет: 24
Пример 5: Множественные возвращаемые значения
Функция может возвращать несколько значений, упакованных в кортеж. Это удобно, когда нужно вернуть несколько результатов:
def get_min_max(numbers):
return min(numbers), max(numbers)
Пример вызова функции:
minimum, maximum = get_min_max([2, 8, 1, 10, 4])
print(f"Минимум: {minimum}, Максимум: {maximum}") # Выведет: Минимум: 1, Максимум: 10
Пример 6: Вложенные функции
Функции могут быть определены внутри других функций. Эти внутренние функции могут использовать переменные из внешних функций, но доступны только внутри внешней функции.
def outer_function(x):
def inner_function(y):
return y * y
return inner_function(x) + x
Пример использования:
result = outer_function(3)
print(result) # Выведет: 12
Пример 7: Функции в качестве аргументов
Функции могут передаваться как аргументы другим функциям. Это полезно для создания гибких и универсальных решений:
def apply_function(func, value):
return func(value)
def square(x):
return x * x
result = apply_function(square, 5)
print(result) # Выведет: 25
Пример 8: Функции как возвращаемое значение
Функция может возвращать другую функцию. Это позволяет создавать функции, которые настраиваются в зависимости от переданных параметров.
def make_multiplier(multiplier):
def multiplier_function(x):
return x * multiplier
return multiplier_function
Использование:
doubler = make_multiplier(2)
tripler = make_multiplier(3)
print(doubler(5)) # Выведет: 10
print(tripler(5)) # Выведет: 15
Пример 9: Функции для обработки данных
Функции часто используются для обработки данных. Рассмотрим функцию, которая фильтрует список чисел, оставляя только чётные:
def filter_even(numbers):
return [num for num in numbers if num % 2 == 0]
Пример использования функции:
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter_even(numbers)
print(even_numbers) # Выведет: [2, 4, 6]
Пример 10: Функции с несколькими операциями
Функции могут выполнять несколько операций. Например, функция, которая сначала вычисляет сумму двух чисел, а затем возвращает её квадрат:
def sum_and_square(a, b):
sum_result = a + b
return sum_result ** 2
Использование:
result = sum_and_square(3, 4)
print(result) # Выведет: 49
Пример 11: Функции для работы с коллекциями
Функции могут использоваться для работы с коллекциями данных. Рассмотрим функцию, которая находит среднее значение чисел в списке:
def average(numbers):
return sum(numbers) / len(numbers)
Использование функции:
numbers = [10, 20, 30, 40, 50]
avg = average(numbers)
print(avg) # Выведет: 30.0
Пример 12: Функции для обработки строк
Функции могут использоваться для обработки строк. Например, функция, которая преобразует строку в верхний регистр и возвращает её длину:
def process_string(text):
upper_text = text.upper()
return len(upper_text)
Использование функции:
length = process_string("hello world")
print(length) # Выведет: 11
Роль print
в отладке
Важно понимать, что функция print
в Python в основном используется для отладки. Она позволяет увидеть результаты выполнения функции или промежуточные значения. Однако функция print
сама по себе не влияет на то, что делает функция. В случаях, когда функция уже возвращает результат или выполняет необходимые действия, использование print
может быть не нужно.
Пример 1: Использование print
для отладки
Если функция возвращает результат, вы можете использовать print
, чтобы увидеть этот результат во время отладки:
def calculate_sum(a, b):
return a + b
result = calculate_sum(5, 7)
print(result) # Выведет: 12
Пример 2: Функция с выводом данных
Если функция уже выполняет вывод данных, использование print
может быть избыточным:
def greet(name):
print(f"Привет, {name}!")
greet("Анна") # Выведет: Привет, Анна!
В этом случае функция уже выполняет свою основную задачу — выводит сообщение. Добавление print
для отладки не требуется, поскольку функция уже делает то, что должна.
Пример 3: Функция с возвращаемым значением
Если функция возвращает значение, можно просто использовать это значение, а не выводить его с помощью print
:
def multiply(a, b):
return a * b
result = multiply(3, 4)
# Результат можно использовать в дальнейшем, например:
final_result = result + 10
print(final_result) # Выведет: 22
Основы аннотаций функций
Аннотации записываются в сигнатуре функции после имени параметра, а также после оператора ->
для указания типа возвращаемого значения. Они могут использоваться для обозначения типов данных, которые ожидаются в аргументах и возвращаемом значении.
def add(x: int, y: int) -> int:
return x + y
В этом примере:
x: int
означает, что аргументx
должен быть типаint
.y: int
— аргументy
также должен быть типаint
.-> int
показывает, что функция возвращает значение типаint
.
2. Синтаксис аннотаций
Аннотации могут быть любыми выражениями. Чаще всего используются аннотации для указания типов данных, но можно использовать любые объекты: строки, списки, словари, функции, классы и т. д.
Пример с разными типами данных:
def greet(name: str, age: int) -> str:
return f"Hello, {name}! You are {age} years old."
Здесь:
name: str
— ожидается строка.age: int
— ожидается целое число.-> str
— функция возвращает строку.
3. Необязательность аннотаций
Аннотации не обязательны и служат только для документации и подсказок. Python не проверяет фактический тип переданных аргументов. Например, в следующем примере функция работает, даже если передать аргументы не того типа:
def multiply(a: int, b: int) -> int:
return a * b
# Передаём строки вместо чисел
result = multiply("3", "5") # Работает, вернёт "33333"
4. Использование None
и необязательных параметров
Для параметров, которые могут быть необязательными или принимать значение None
, также можно указывать аннотации. В таких случаях часто используется тип Optional
, который позволяет параметру быть либо указанного типа, либо None
.
Пример:
from typing import Optional
def display_info(name: str, age: Optional[int] = None) -> str:
if age is None:
return f"{name}, age not provided"
return f"{name} is {age} years old"
Здесь параметр age
может быть как целым числом, так и None
, что делает его необязательным.
5. Аннотации сложных типов данных
Python позволяет аннотировать сложные типы данных, такие как списки, словари, множества и т. д. Для этого можно использовать типы из модуля typing
: List
, Dict
, Set
, Tuple
и другие.
Пример для списка:
from typing import List
def process_numbers(numbers: List[int]) -> int:
return sum(numbers)
Здесь:
numbers: List[int]
означает, что параметрnumbers
должен быть списком целых чисел (int
).- Функция возвращает целое число (
-> int
), которое является суммой элементов списка.
Пример для словаря:
from typing import Dict
def count_items(data: Dict[str, int]) -> int:
return sum(data.values())
Здесь:
data: Dict[str, int]
— словарь, где ключи — строки, а значения — целые числа.
6. Аннотации с кортежами и другими коллекциями
Для аннотаций кортежей и других структур данных также используется модуль typing
. В следующем примере показано, как аннотировать кортеж:
from typing import Tuple
def get_coordinates() -> Tuple[float, float]:
return (45.0, 90.0)
Здесь:
- Функция возвращает кортеж из двух элементов типа
float
.
7. Аннотации функций с переменным числом аргументов
Для функций, принимающих переменное количество аргументов, можно аннотировать как позиционные аргументы *args
, так и именованные аргументы **kwargs
.
Пример:
from typing import Any
def print_all(*args: Any, **kwargs: Any) -> None:
for arg in args:
print(arg)
for key, value in kwargs.items():
print(f"{key}: {value}")
Здесь:
*args: Any
— функция может принимать любое количество аргументов любого типа.**kwargs: Any
— функция может принимать любое количество именованных аргументов любого типа.- Функция ничего не возвращает, поэтому аннотация возвращаемого значения —
None
.
8. Инспекция аннотаций
Аннотации хранятся в специальном атрибуте функции __annotations__
, который является словарём, где ключи — имена аргументов, а значения — аннотации.
Пример:
def add(x: int, y: int) -> int:
return x + y
print(add.__annotations__)
# Вывод: {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
Этот атрибут можно использовать для динамического анализа типов и создания документации.
9. Аннотации в Python 3.9+ и 3.10+
С выходом Python 3.9 аннотации стали ещё удобнее. Теперь вместо использования типов из typing
, таких как List
и Dict
, можно использовать встроенные типы напрямую.
Пример для Python 3.9+:
def process_numbers(numbers: list[int]) -> int:
return sum(numbers)
В Python 3.10 появилась поддержка оператора объединения типов |
. Это удобная альтернатива Optional
:
def display_info(name: str, age: int | None = None) -> str:
if age is None:
return f"{name}, age not provided"
return f"{name} is {age} years old"
Здесь int | None
— это то же самое, что и Optional[int]
.
10. Аннотации и типизация в реальном мире
Несмотря на то что Python остаётся динамически типизированным языком, аннотации играют важную роль в современном программировании. Они помогают:
- Улучшить читаемость кода и упростить его поддержку.
- Использовать инструменты статической проверки типов, такие как
mypy
. - Ускорить разработку за счёт автодополнения в IDE.
- Избежать некоторых типов ошибок благодаря более строгим проверкам типов.
Аннотации позволяют программистам писать более надёжный код, делая его понятным не только для человека, но и для инструментов автоматической проверки.
Вот таблица, обобщающая основные концепции определения функций в Python, их использование, отладку и документацию:
Раздел | Описание | Примеры |
---|---|---|
Определение функции | Функция определяется с помощью ключевого слова def , имени функции, круглых скобок и двоеточия. |
def greet(name): ... |
Простая функция без аргументов | Функция, которая не принимает аргументов и просто выполняет действие. | def say_hello(): print("Привет, мир!") |
Функция с аргументами | Функция, которая принимает аргументы и использует их в своём теле. | def greet(name): print(f"Привет, {name}!") |
Функция с несколькими аргументами | Функция, которая принимает несколько аргументов. | def add(a, b): return a + b |
Возвращаемое значение | Функция может возвращать значение с помощью return . |
def multiply(x, y): return x * y |
Множественные возвращаемые значения | Функция возвращает несколько значений, упакованных в кортеж. | def get_min_max(numbers): return min(numbers), max(numbers) |
Вложенные функции | Функции могут содержать другие функции внутри, доступные только в рамках внешней функции. | def outer_function(x): def inner_function(y): return y * y |
Функции в качестве аргументов | Функции могут передаваться в другие функции в качестве аргументов. | def apply_function(func, value): return func(value) |
Функции как возвращаемое значение | Функция может возвращать другую функцию. | def make_multiplier(multiplier): def multiplier_function(x): return x * multiplier |
Функции для обработки данных | Функции часто используются для обработки и фильтрации данных. | def filter_even(numbers): return [num for num in numbers if num % 2 == 0] |
Функции с несколькими операциями | Функции могут выполнять несколько операций. | def sum_and_square(a, b): sum_result = a + b; return sum_result ** 2 |
Функции для работы с коллекциями | Функции могут использоваться для работы с коллекциями данных, например, для вычисления среднего значения. | def average(numbers): return sum(numbers) / len(numbers) |
Функции для обработки строк | Функции могут использоваться для обработки строк, например, преобразования в верхний регистр. | def process_string(text): upper_text = text.upper(); return len(upper_text) |
Роль print в отладке |
Функция print используется для отладки, чтобы увидеть результаты или промежуточные значения. |
def calculate_sum(a, b): return a + b; print(calculate_sum(5, 7)) |
Документация функции | Документация добавляется сразу после определения функции и улучшает читаемость кода. | def divide(a, b): """ Делит число a на число b. ... """ return a / b |
Эта таблица охватывает основные аспекты работы с функциями в Python и включает примеры, которые помогут вам понять, как применять эти концепции на практике.
Функции являются основным инструментом для упрощения и структурирования кода в Python. Они позволяют выполнять повторяющиеся задачи, работать с различными данными и возвращать результаты для дальнейшего использования. Понимание основ определения функций, их аргументов и возвращаемых значений поможет вам создавать более эффективные и читаемые программы. Также важно помнить, что print
предназначен в первую очередь для отладки, и использование его в функциях, которые уже возвращают или выводят данные, может быть необязательно.