ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 05.12.2023
Просмотров: 855
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Глава 11. Пользовательские функции
223
Листинг 11.26. Ограничение доступа с помощью декоратора passw = input("Введите пароль: ") def test_passw(p): def deco(f): if p == "10": # Сравниваем пароли return f else: return lambda: "Доступ закрыт" return deco # Возвращаем функцию-декоратор
@test_passw(passw) def func(): return "Доступ открыт" print(func()) # Вызываем функцию
Здесь после символа
@
указана не ссылка на функцию, а выражение, которое возвращает декоратор. Иными словами, декоратором является не функция test_passw()
, а результат ее выполнения (функция deco()
). Если введенный пароль является правильным, то выполнит- ся функция func()
, в противном случае будет выведена надпись "Доступ закрыт"
, которую вернет анонимная функция.
11.8. Рекурсия. Вычисление факториала
Рекурсия — это возможность функции вызывать саму себя. Рекурсию удобно использовать для перебора объекта, имеющего заранее неизвестную структуру, или для выполнения не- определенного количества операций. В качестве примера рассмотрим вычисление факто- риала (листинг 11.27).
Листинг 11.27. Вычисление факториала def factorial(n): if n == 0 or n == 1: return 1 else: return n * factorial(n - 1) while True: x = input("Введите число: ") if x.isdigit(): # Если строка содержит только цифры x = int(x) # Преобразуем строку в число break # Выходим из цикла else: print("Вы ввели не число!") print("Факториал числа {0} = {1}".format(x, factorial(x)))
Впрочем, проще всего для вычисления факториала воспользоваться функцией factorial()
из модуля math
:
224
Часть I. Основы языка Python
>>> import math
>>> math.factorial(5), math.factorial(6)
(120, 720)
Количество вызовов функции самой себя (проходов рекурсии) ограничено. Узнать его мож- но, вызвав функцию getrecursionlimit()
из модуля sys
:
>>> import sys
>>> sys.getrecursionlimit()
1000
При превышении допустимого количества проходов рекурсии будет возбуждено исклю- чение:
RuntimeError
— в версиях Python, предшествующих 3.5;
RecursionError
— в Python 3.5 и последующих версиях.
11.9. Глобальные и локальные переменные
Глобальные переменные — это переменные, объявленные в программе вне функции.
В Python глобальные переменные видны в любой части модуля, включая функции (лис- тинг 11.28).
Листинг 11.28. Глобальные переменные def func(glob2): print("Значение глобальной переменной glob1 =", glob1) glob2 += 10 print("Значение локальной переменной glob2 =", glob2) glob1, glob2 = 10, 5 func(77) # Вызываем функцию print("Значение глобальной переменной glob2 =", glob2)
Результат выполнения:
Значение глобальной переменной glob1 = 10
Значение локальной переменной glob2 = 87
Значение глобальной переменной glob2 = 5
Переменной glob2
внутри функции присваивается значение, переданное при вызове функ- ции. В результате создается новая переменная с тем же именем, но являющаяся локальной.
Все изменения этой переменной внутри функции не затронут значение одноименной гло- бальной переменной.
Локальные переменные — это переменные, объявляемые внутри функций. Если имя ло- кальной переменной совпадает с именем глобальной переменной, то все операции внутри функции осуществляются с локальной переменной, а значение глобальной переменной не изменяется. Локальные переменные видны только внутри тела функции (листинг 11.29).
Глава 11. Пользовательские функции
225
Листинг 11.29. Локальные переменные def func(): local1 = 77 # Локальная переменная glob1 = 25 # Локальная переменная print("Значение glob1 внутри функции =", glob1) glob1 = 10 # Глобальная переменная func() # Вызываем функцию print("Значение glob1 вне функции =", glob1) try: print(local1) # Вызовет исключение NameError except NameError: # Обрабатываем исключение print("Переменная local1 не видна вне функции")
Результат выполнения:
Значение glob1 внутри функции = 25
Значение glob1 вне функции = 10
Переменная local1 не видна вне функции
Как видно из примера, переменная local1
, объявленная внутри функции func()
, недоступна вне функции. Объявление внутри функции локальной переменной glob1
не изменило зна- чения одноименной глобальной переменной.
Если обращение к переменной производится до присваивания ей значения (даже если суще- ствует одноименная глобальная переменная), будет возбуждено исключение
UnboundLocalError
(листинг 11.30).
Листинг 11.30. Ошибка при обращении к переменной до присваивания значения def func():
# Локальная переменная еще не определена print(glob1) # Эта строка вызовет ошибку!!! glob1 = 25 # Локальная переменная glob1 = 10 # Глобальная переменная func() # Вызываем функцию
# Результат выполнения:
# UnboundLocalError: local variable 'glob1' referenced before assignment
Для того чтобы значение глобальной переменной можно было изменить внутри функции, необходимо объявить переменную глобальной с помощью ключевого слова global
. Проде- монстрируем это на примере (листинг 11.31).
Листинг 11.31. Использование ключевого слова global def func():
# Объявляем переменную glob1 глобальной global glob1 glob1 = 25 # Изменяем значение глобальной переменной print("Значение glob1 внутри функции =", glob1)
226
Часть I. Основы языка Python glob1 = 10 # Глобальная переменная print("Значение glob1 вне функции =", glob1) func() # Вызываем функцию print("Значение glob1 после функции =", glob1)
Результат выполнения:
Значение glob1 вне функции = 10
Значение glob1 внутри функции = 25
Значение glob1 после функции = 25
Таким образом, поиск идентификатора, используемого внутри функции, будет произво- диться в следующем порядке:
1. Поиск объявления идентификатора внутри функции (в локальной области видимости).
2. Поиск объявления идентификатора в глобальной области.
3. Поиск во встроенной области видимости (встроенные функции, классы и т. д.).
При использовании анонимных функций следует учитывать, что при указании внутри функции глобальной переменной будет сохранена ссылка на эту переменную, а не ее значе- ние в момент определения функции:
>>> x = 5
>>> func = lambda: x # Сохраняется ссылка, а не значение переменной x!!!
>>> x = 80 # Изменили значение
>>> print(func()) # Выведет: 80, а не 5
Если необходимо сохранить именно текущее значение переменной, можно воспользоваться способом, приведенным в листинге 11.32.
Листинг 11.32. Сохранение значения переменной x = 5 func = (lambda y: lambda: y)(x) # Сохраняется значение переменной x x = 80 # Изменили значение print(func()) # Выведет: 5
Обратите внимание на вторую строку примера. В ней мы определили анонимную функцию с одним параметром, возвращающую ссылку на вложенную анонимную функцию. Далее мы вызываем первую функцию с помощью круглых скобок и передаем ей значение пере- менной x
. В результате сохраняется текущее значение переменной, а не ссылка на нее.
Сохранить текущее значение переменной также можно, указав глобальную переменную в качестве значения параметра по умолчанию в определении функции (листинг 11.33).
Листинг 11.33. Сохранение значения с помощью параметра по умолчанию x = 5 func = lambda x=x: x # Сохраняется значение переменной x x = 80 # Изменили значение print(func()) # Выведет: 5
Глава 11. Пользовательские функции
227
Получить все идентификаторы и их значения позволяют следующие функции:
globals()
— возвращает словарь с глобальными идентификаторами;
locals()
— возвращает словарь с локальными идентификаторами.
Пример использования обеих этих функций показан в листинге 11.34.
Листинг 11.34. Использование функций globals() и locals() def func(): local1 = 54 glob2 = 25 print("Глобальные идентификаторы внутри функции") print(sorted(globals().keys())) print("Локальные идентификаторы внутри функции") print(sorted(locals().keys())) glob1, glob2 = 10, 88 func() print("Глобальные идентификаторы вне функции") print(sorted(globals().keys()))
Результат выполнения:
Глобальные идентификаторы внутри функции
['__annotations__', '__builtins__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'func', 'glob1', 'glob2']
Локальные идентификаторы внутри функции
['glob2', 'local1']
1 ... 17 18 19 20 21 22 23 24 ... 83
Глобальные идентификаторы вне функции
['__annotations__', '__builtins__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'func', 'glob1', 'glob2']
vars([<Объект>])
— если вызывается без параметра внутри функции, возвращает сло- варь с локальными идентификаторами. Если вызывается без параметра вне функции, возвращает словарь с глобальными идентификаторами. При указании объекта в качестве параметра возвращает идентификаторы этого объекта (эквивалентно вызову
<Объект>.__ dict__
). Пример использования этой функции можно увидеть в листинге 11.35.
Листинг 11.35. Использование функции vars() def func(): local1 = 54 glob2 = 25 print("Локальные идентификаторы внутри функции") print(sorted(vars().keys())) glob1, glob2 = 10, 88 func() print("Глобальные идентификаторы вне функции") print(sorted(vars().keys())) print("Указание объекта в качестве параметра") print(sorted(vars(dict).keys())) print("Альтернативный вызов") print(sorted(dict.__dict__.keys()))
228
Часть I. Основы языка Python
11.10. Вложенные функции
Одну функцию можно вложить в другую функцию, причем уровень вложенности не огра- ничен. При этом вложенная функция получает свою собственную локальную область види- мости и имеет доступ к переменным, объявленным внутри функции, в которую она вложена
(функции-родителя). Рассмотрим вложение функций на примере (листинг 11.36).
Листинг 11.36. Вложенные функции def func1(x): def func2(): print(x) return func2 f1 = func1(10) f2 = func1(99) f1() # Выведет: 10 f2() # Выведет: 99
Здесь мы определили функцию func1()
, принимающую один параметр, а внутри нее — вложенную функцию func2()
. Результатом выполнения функции func1()
будет ссылка на эту вложенную функцию. Внутри функции func2()
мы производим вывод значения пере- менной x
, которая является локальной в функции func1()
. Таким образом, помимо локаль- ной, глобальной и встроенной областей видимости, добавляется вложенная область види- мости. При этом поиск идентификаторов вначале производится внутри вложенной функ- ции, затем внутри функции-родителя, далее в функциях более высокого уровня и лишь потом в глобальной и встроенной областях видимости. В нашем примере переменная x
бу- дет найдена в области видимости функции func1()
Следует учитывать, что в момент определения функции сохраняются ссылки на перемен- ные, а не их значения. Например, если после определения функции func2()
произвести из- менение переменной x
, то будет использоваться это новое значение (листинг 11.37).
Листинг 11.37. При объявлении вложенной функции сохраняется ссылка на переменную def func1(x): def func2(): print(x) x = 30 return func2 f1 = func1(10) f2 = func1(99) f1() # Выведет: 30 f2() # Выведет: 30
Обратите внимание на результат выполнения. В обоих случаях мы получили значение
30
Если необходимо сохранить именно значение переменной при определении вложенной функции, следует передать значение как значение по умолчанию (листинг 11.38).
Глава 11. Пользовательские функции
229
Листинг 11.38. Принудительное сохранение значения переменной def func1(x): def func2(x=x): # Сохраняем текущее значение, а не ссылку print(x) x = 30 return func2 f1 = func1(10) f2 = func1(99) f1() # Выведет: 10 f2() # Выведет: 99
Теперь попробуем из вложенной функции func2()
изменить значение переменной x
, объяв- ленной внутри функции func1()
. Если внутри функции func2()
присвоить значение пере- менной x
, будет создана новая локальная переменная с таким же именем. Если внутри функции func2()
объявить переменную как глобальную и присвоить ей значение, то изме- нится значение глобальной переменной, а не значение переменной x
внутри функции func1()
. Таким образом, ни один из изученных ранее способов не позволяет из вложенной функции изменить значение переменной, объявленной внутри функции-родителя. Чтобы решить эту проблему, следует объявить необходимые переменные с помощью ключевого слова nonlocal
(листинг 11.39).
Листинг 11.39. Ключевое слово nonlocal def func1(a): x = a def func2(b): nonlocal x # Объявляем переменную как nonlocal print(x) x = b # Можем изменить значение x в func1() return func2 f = func1(10) f(5) # Выведет: 10 f(12) # Выведет: 5 f(3) # Выведет: 12
При использовании ключевого слова nonlocal следует помнить, что переменная обязатель- но должна существовать внутри функции-родителя. В противном случае будет выведено сообщение об ошибке.
11.11. Аннотации функций
В Python функции могут содержать аннотации, которые вводят новый способ документи- рования. Теперь в заголовке функции допускается указывать предназначение каждого параметра, его тип данных, а также тип возвращаемого функцией значения. Аннотации имеют следующий формат:
230
Часть I. Основы языка Python def <Имя функции>(
[<Параметр1>[: <Выражение>] [= <Значение по умолчанию>][, ...,
<ПараметрN>[: <Выражение>] [= <Значение по умолчанию>]]]
) -> <Возвращаемое значение>:
<Тело функции>
В параметрах
<Выражение>
и
<Возвращаемое значение>
можно указать любое допустимое выражение языка Python. Это выражение будет выполнено при создании функции.
Пример указания аннотаций:
>>> def func(a: "Параметр1", b: 10 + 5 = 3) -> None: print(a, b)
В этом примере для переменной a
создано описание "Параметр1"
. Для переменной b
выра- жение
10 + 5
является описанием, а число
3
— значением параметра по умолчанию. Кроме того, после закрывающей круглой скобки указан тип возвращаемого функцией значения:
None
. После создания функции все выражения будут выполнены, и результаты сохранятся в виде словаря в атрибуте
__annotations__
объекта функции.
Для примера выведем значение этого атрибута:
>>> def func(a: "Параметр1", b: 10 + 5 = 3) -> None: pass
>>> func.__annotations__
{'a': 'Параметр1', 'b': 15, 'return': None}