ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 05.12.2023
Просмотров: 872
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Глава 11. Пользовательские функции
215
Листинг 11.8. Сопоставление по ключам def summa(x, y): return x + y print(summa(y=20, x=10)) # Сопоставление по ключам
Сопоставление по ключам очень удобно использовать, если функция имеет несколько не- обязательных параметров. В этом случае не нужно указывать все значения, а достаточно присвоить значение нужному параметру:
>>> def summa(a=2, b=3, c=4): # Все параметры являются необязательными return a + b + c
>>> print(summa(2, 3, 20)) # Позиционное присваивание
>>> print(summa(c=20)) # Сопоставление по ключам
Если значения параметров, которые планируется передать в функцию, содержатся в корте- же или списке, то перед этим кортежем или списком следует указать символ
*
. Пример передачи значений из кортежа и списка приведен в листинге 11.9.
Листинг 11.9. Пример передачи значений из кортежа и списка def summa(a, b, c): return a + b + c t1, arr = (1, 2, 3), [1, 2, 3] print(summa(*t1)) # Распаковываем кортеж print(summa(*arr)) # Распаковываем список t2 = (2, 3) print(summa(1, *t2)) # Можно комбинировать значения
Если значения параметров содержатся в словаре, то перед ним следует поставить две звез- дочки:
**
(листинг 11.10).
Листинг 11.10. Пример передачи значений из словаря def summa(a, b, c): return a + b + c d1 = {"a": 1, "b": 2, "c": 3} print(summa(**d1)) # Распаковываем словарь t, d2 = (1, 2), {"c": 3} print(summa(*t, **d2)) # Можно комбинировать значения
Объекты в функцию передаются по ссылке. Если объект относится к неизменяемому типу, то изменение значения внутри функции не затронет значение переменной вне функции:
>>> def func(a, b): a, b = 20, "str"
>>> x, s = 80, "test"
>>> func(x, s) # Значения переменных x и s не изменяются
>>> print(x, s) # Выведет: 80 test
В этом примере значения в переменных x
и s
не изменились. Однако, если объект относится к изменяемому типу, ситуация будет другой:
216
Часть I. Основы языка Python
>>> def func(a, b): a[0], b["a"] = "str", 800
>>> x = [1, 2, 3] # Список
>>> y = {"a": 1, "b": 2} # Словарь
>>> func(x, y) # Значения будут изменены!!!
>>> print(x, y) # Выведет: ['str', 2, 3] {'a': 800, 'b': 2}
Как видно из примера, значения в переменных x
и y
изменились, поскольку список и сло- варь относятся к изменяемым типам. Чтобы избежать изменения значений, внутри функции следует создать копию объекта (листинг 11.11).
Листинг 11.11. Передача изменяемого объекта в функцию def func(a, b): a = a[:] # Создаем поверхностную копию списка b = b.copy() # Создаем поверхностную копию словаря a[0], b["a"] = "str", 800 x = [1, 2, 3] # Список y = {"a": 1, "b": 2} # Словарь func(x, y) # Значения останутся прежними print(x, y) # Выведет: [1, 2, 3] {'a': 1, 'b': 2}
Можно также передать копию объекта непосредственно в вызове функции: func(x[:], y.copy())
Если указать объект, имеющий изменяемый тип, в качестве значения параметра по умолча- нию, этот объект будет сохраняться между вызовами функции:
>>> def func(a=[]): a.append(2) return a
>>> print(func()) # Выведет: [2]
>>> print(func()) # Выведет: [2, 2]
>>> print(func()) # Выведет: [2, 2, 2]
Как видно из примера, значения накапливаются внутри списка. Обойти эту проблему мож- но, например, следующим образом:
>>> def func(a=None):
# Создаем новый список, если значение равно None if a is None: a = [] a.append(2) return a
>>> print(func()) # Выведет: [2]
>>> print(func([1])) # Выведет: [1, 2]
>>> print(func()) # Выведет: [2]
11.4. Переменное число параметров в функции
Если перед параметром в определении функции указать символ
*
, то функции можно будет передать произвольное количество параметров. Все переданные параметры сохраняются
Глава 11. Пользовательские функции
217 в кортеже. Для примера напишем функцию суммирования произвольного количества чисел
(листинг 11.12).
Листинг 11.12. Передача функции произвольного количества параметров def summa(*t):
""" Функция принимает произвольное количество параметров """ res = 0 for i in t: # Перебираем кортеж с переданными параметрами res += i return res print(summa(10, 20)) # Выведет: 30 print(summa(10, 20, 30, 40, 50, 60)) # Выведет: 210
Можно также вначале указать несколько обязательных параметров и параметров, имеющих значения по умолчанию (листинг 11.13).
Листинг 11.13. Функция с параметрами разных типов def summa(x, y=5, *t): # Комбинация параметров res = x + y for i in t: # Перебираем кортеж с переданными параметрами res += i return res print(summa(10)) # Выведет: 15 print(summa(10, 20, 30, 40, 50, 60)) # Выведет: 210
Если перед параметром в определении функции указать две звездочки:
**
, то все именован- ные параметры будут сохранены в словаре (листинг 11.14).
Листинг 11.14. Сохранение переданных данных в словаре def func(**d): for i in d: # Перебираем словарь с переданными параметрами print("{0} => {1}".format(i, d[i]), end=" ") func(a=1, b=2, c=3) # Выведет: a => 1 c => 3 b => 2
При комбинировании параметров параметр с двумя звездочками записывается самым по- следним. Если в определении функции указывается комбинация параметров с одной звез- дочкой и двумя звездочками, то функция примет любые переданные ей параметры (лис- тинг 11.15).
Листинг 11.15. Комбинирование параметров def func(*t, **d):
""" Функция примет любые параметры """ for i in t: print(i, end=" ") for i in d: # Перебираем словарь с переданными параметрами print("{0} => {1}".format(i, d[i]), end=" ")
218
Часть I. Основы языка Python func(35, 10, a=1, b=2, c=3) # Выведет: 35 10 a => 1 c => 3 b => 2 func(10) # Выведет: 10 func(a=1, b=2) # Выведет: a => 1 b => 2
В определении функции можно указать, что некоторые параметры передаются только по именам. Такие параметры должны указываться после параметра с одной звездочкой, но перед параметром с двумя звездочками. Именованные параметры могут иметь значения по умолчанию:
1 ... 16 17 18 19 20 21 22 23 ... 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).