Главная страница

ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница21 из 83
1   ...   17   18   19   20   21   22   23   24   ...   83
>>> def func(*t, a, b=10, **d): print(t, a, b, d)
>>> func(35, 10, a=1, c=3) # Выведет: (35, 10) 1 10 {'c': 3}
>>> func(10, a=5) # Выведет: (10,) 5 10 {}
>>> func(a=1, b=2) # Выведет: () 1 2 {}
>>> func(1, 2, 3) # Ошибка. Параметр a обязателен!
В этом примере переменная t
примет любое количество значений, которые будут объеди- нены в кортеж. Переменные a
и b
должны передаваться только по именам, причем перемен- ной a
при вызове функции обязательно нужно передать значение. Переменная b
имеет зна- чение по умолчанию, поэтому при вызове допускается не передавать ей значение, но если значение передается, оно должно быть указано после названия параметра и оператора
=
Переменная d
примет любое количество именованных параметров и сохранит их в словаре.
Обратите внимание на то, что хотя переменные a
и b
являются именованными, они не попа- дут в этот словарь.
Параметра с двумя звездочками в определении функции может не быть, а вот параметр с одной звездочкой при указании параметров, передаваемых только по именам, должен присутствовать обязательно. Если функция не должна принимать переменного количества параметров, но необходимо использовать переменные, передаваемые только по именам, то можно указать только звездочку без переменной:
>>> def func(x=1, y=2, *, a, b=10): print(x, y, a, b)
>>> func(35, 10, a=1) # Выведет: 35 10 1 10
>>> func(10, a=5) # Выведет: 10 2 5 10
>>> func(a=1, b=2) # Выведет: 1 2 1 2
>>> func(a=1, y=8, x=7) # Выведет: 7 8 1 10
>>> func(1, 2, 3) # Ошибка. Параметр a обязателен!
В этом примере значения переменным x
и y
можно передавать как по позициям, так и по именам. Поскольку переменные имеют значения по умолчанию, допускается вообще не передавать им значений. Переменные a
и b
расположены после параметра с одной звездоч- кой, поэтому передать значения при вызове можно только по именам. Так как переменная b
имеет значение по умолчанию, допускается не передавать ей значение при вызове, а вот переменная a
обязательно должна получить значение, причем только по имени.
11.5. Анонимные функции
Помимо обычных, язык Python позволяет использовать анонимные функции, которые также называются лямбда-функциями. Анонимная функция не имеет имени и описывается с по- мощью ключевого слова lambda в следующем формате: lambda [<Параметр1>[, ..., <ПараметрN>]]: <Возвращаемое значение>

Глава 11. Пользовательские функции
219
После ключевого слова lambda можно указать передаваемые параметры. В качестве пара- метра
<Возвращаемое значение>
указывается выражение, результат выполнения которого будет возвращен функцией.
В качестве значения анонимная функция возвращает ссылку на объект-функцию, которую можно сохранить в переменной или передать в качестве параметра другой функции. Вы- звать анонимную функцию можно, как и обычную, с помощью круглых скобок, внутри ко- торых расположены передаваемые параметры. Пример использования анонимных функций приведен в листинге 11.16.
Листинг 11.16. Пример использования анонимных функций f1 = lambda: 10 + 20 # Функция без параметров f2 = lambda x, y: x + y # Функция с двумя параметрами f3 = lambda x, y, z: x + y + z # Функция с тремя параметрами print(f1()) # Выведет: 30 print(f2(5, 10)) # Выведет: 15 print(f3(5, 10, 30)) # Выведет: 45
Как и у обычных функций, некоторые параметры анонимных функций могут быть необяза- тельными. Для этого параметрам в определении функции присваивается значение по умол- чанию (листинг 11.17).
Листинг 11.17. Необязательные параметры в анонимных функциях f = lambda x, y=2: x + y print(f(5)) # Выведет: 7 print(f(5, 6)) # Выведет: 11
Чаще всего анонимную функцию не сохраняют в переменной, а сразу передают в качестве параметра в другую функцию. Например, метод списков sort()
позволяет указать пользо- вательскую функцию в параметре key
. Отсортируем список без учета регистра символов, указав в качестве параметра анонимную функцию (листинг 11.18).
Листинг 11.18. Сортировка без учета регистра символов arr = ["единица1", "Единый", "Единица2"] arr.sort(key=lambda s: s.lower()) for i in arr: print(i, end=" ")
# Результат выполнения: единица1 Единица2 Единый
11.6. Функции-генераторы
Функцией-генератором называется функция, которая при последовательных вызовах воз- вращает очередной элемент какой-либо последовательности. Приостановить выполнение функции и превратить функцию в генератор позволяет ключевое слово yield
. Для примера напишем функцию, которая возводит элементы последовательности в указанную степень
(листинг 11.19).

220
Часть I. Основы языка Python
Листинг 11.19. Пример использования функций-генераторов def func(x, y): for i in range(1, x+1): yield i ** y for n in func(10, 2): print(n, end=" ") # Выведет: 1 4 9 16 25 36 49 64 81 100 print() # Вставляем пустую строку for n in func(10, 3): print(n, end=" ") # Выведет: 1 8 27 64 125 216 343 512 729 1000
Функции-генераторы поддерживают метод
__next__()
, который позволяет получить сле- дующее значение. Когда значения заканчиваются, метод возбуждает исключение
StopIteration
. Вызов метода
__next__()
в цикле for производится незаметно для нас. Для примера перепишем предыдущую программу, использовав метод
__next__()
вместо цикла for
(листинг 11.20).
Листинг 11.20. Использование метода __next__() def func(x, y): for i in range(1, x+1): yield i ** y i = func(3, 3) print(i.__next__()) # Выведет: 1 (1 ** 3) print(i.__next__()) # Выведет: 8 (2 ** 3) print(i.__next__()) # Выведет: 27 (3 ** 3) print(i.__next__()) # Исключение StopIteration
Получается, что с помощью обычных функций мы можем вернуть все значения сразу в виде списка, а с помощью функций-генераторов — только одно значение за раз. Эта особенность очень полезна при обработке большого количества значений, т. к. не понадобится загружать весь список со значениями в память.
Существует возможность вызвать одну функцию-генератор из другой. Для этого применя- ется расширенный синтаксис ключевого слова yield
: yield from <Вызываемая функция-генератор>
Рассмотрим следующий пример. Пусть у нас есть список чисел, и нам требуется получить другой список, включающий числа в диапазоне от 1 до каждого из чисел в первом списке.
Чтобы создать такой список, напишем код, показанный в листинге 11.21.
Листинг 11.21. Вызов одной функции-генератора из другой (простой случай) def gen(l): for e in l: yield from range(1, e + 1) l = [5, 10] for i in gen([5, 10]): print(i, end = " ")

Глава 11. Пользовательские функции
221
Здесь мы в функции-генераторе gen перебираем переданный ей в качестве параметра список и для каждого его элемента вызываем другую функцию-генератор. В качестве последней выступает выражение, создающее диапазон от 1 до значения очередного элемента, увели- ченного на единицу (чтобы это значение вошло в диапазон). В результате на выходе мы получим вполне ожидаемый результат:
1 2 3 4 5 1 2 3 4 5 6 7 8 9 10
Усложним задачу, включив в результирующий список числа, умноженные на 2. Код, вы- полняющий эту задачу, показан в листинге 11.22.
Листинг 11.22. Вызов одной функции-генератора из другой (сложный случай) def gen2(n): for e in range(1, n + 1): yield e * 2 def gen(l): for e in l: yield from gen2(e) l = [5, 10] for i in gen([5, 10]): print(i, end = " ")
Здесь мы вызываем из функции-генератора gen написанную нами самими функцию- генератор gen2
. Последняя создает диапазон, перебирает все входящие в него числа и воз- вращает их умноженными на 2. Результат работы приведенного в листинге кода таков:
2 4 6 8 10 2 4 6 8 10 12 14 16 18 20
Что нам и требовалось получить.
11.7. Декораторы функций
Декораторы позволяют изменить поведение обычных функций — например, выполнить какие-либо действия перед выполнением функции. Рассмотрим это на примере (лис- тинг 11.23).
Листинг 11.23. Декораторы функций def deco(f): # Функция-декоратор print("Вызвана функция func()") return f # Возвращаем ссылку на функцию
@deco def func(x): return "x = {0}".format(x) print(func(10))
Результат выполнения этого примера:
Вызвана функция func() x = 10

222
Часть I. Основы языка Python
Здесь перед определением функции func()
было указано имя функции deco()
с предваряю- щим символом
@
:
@deco
Таким образом, функция deco()
стала декоратором функции func()
. В качестве параметра функция-декоратор принимает ссылку на функцию, поведение которой необходимо изме- нить, и должна возвращать ссылку на ту же функцию или какую-либо другую. Наш преды- дущий пример эквивалентен коду, показанному в листинге 11.24.
Листинг 11.24. Эквивалент использования декоратора def deco(f): print("Вызвана функция func()") return f def func(x): return "x = {0}".format(x)
# Вызываем функцию func() через функцию deco() print(deco(func)(10))
Перед определением функции можно указать сразу несколько функций-декораторов. Для примера обернем функцию func()
в два декоратора: deco1()
и deco2()
(листинг 11.25).
Листинг 11.25. Указание нескольких декораторов def deco1(f): print("Вызвана функция deco1()") return f def deco2(f): print("Вызвана функция deco2()") return f
@deco1
@deco2 def func(x): return "x = {0}".format(x) print(func(10))
Вот что мы увидим после выполнения примера:
Вызвана функция deco2()
Вызвана функция deco1() x = 10
Использование двух декораторов эквивалентно следующему коду: func = deco1(deco2(func))
Здесь сначала будет вызвана функция deco2()
, а затем функция deco1()
. Результат выпол- нения будет присвоен идентификатору func
В качестве еще одного примера использования декораторов рассмотрим выполнение функ- ции только при правильно введенном пароле (листинг 11.26).

Глава 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


написать администратору сайта