Функции и замыкание
Функции
Пространства имён: повторение
- лямбда-функции (функции-выражения)
Задание функции: формальные и фактические параметры, return
- Duck typing: функция как формализация алгоритма
вызов функции как локальное пространство имён
globals() и locals()
сложный случай определение локальности по связыванию, global
nonlocal для вложенных вызовов
- функция как объект: именование, передача в качестве параметра
- Распаковка и запаковка параметров
- функция с произвольным числом параметров
- Параметры функции по умолчанию (именованные параметры)
(to be continued… они теперь не равны, вообще см. полный вид описания функции)
Про рекурсию
Рекурсия и цикл. Теория vs. практика. Гвидо, Python и хвостовой вызов
- ⇒ максимальная глубина рекурсии
- ⇒ логарифмический критерий уместности рекурсии
Замещение рекурсии стеком. Пример: есть ли среди натуральных чисел Seq такие, что в сумме дают S. Рекурсивный вариант.
- S — неизменяемая часть, Seq, Res — изменяемая, значит, их надо сохранять в стек. Рекурсия — это цикл, которые продолжается до тех пор, пока рекурсивные вызовы не кончились.
- здесь не тот порядок добавления, для полного соответствия надо в обратном
- вместо списковой сборки надо использовать генератор, но у нас их ещё не было ☺
Замыкание
- Функция — это объект
- Её можно изготовить внутри другой функции и вернуть
- …причём в зависимости от параметров этой другой функции!
- …в процессе чего некоторые объекты из ПИ создающей функции «залипают» в ПИ создаваемой
- только они там навсегда должны залипнуть, а не только на время вызова
⇒ .__closure__
- Это и есть замыкание!
Пример:
и
Also: nonlocal name — явное указание брать имя name из внешнего, но не глобального пространства имён
В частности, позднее связывание:
Поскольку i для сгенерированных функций нелокальное, оно попадает в замыканий, и это один и тот же объект во всех adder-ах:
>>> c = create_adders() >>> c[1] <function create_adders.<locals>.adder at 0x7f272d2f93b0> >>> c[1].__closure__ (<cell at 0x7f272d1c1510: int object at 0x7f272db36660>,) >>> c[2].__closure__ (<cell at 0x7f272d1c1510: int object at 0x7f272db36660>,) >>> c[2].__closure__[0].cell_contents 9 >>> c[1].__closure__[0].cell_contents 9
Если мы хотели не этого, надо сделать так, чтобы при создании очередного adder-а его i именовало новый объект:
При этом никакого замыкания не произойдёт, у каждого adder-а будет своё локальное j, инициализированное соответствующим значением i. (Если бы нам нужно было сильнее запутаться, мы могли бы написать i=i вместо j=i ☺ ).
Числа (попытка №2, если успеем)
- Комплексные числа из коробки
import math vs. from math import *
вычисления в рациональных числах с помощью decimal и fractions
decimal.Decimal(1.1) vs. decimal.Decimal("1.1")
fractions.Fraction(1/3) vs. fractions.Fraction(1,3)
(если уж совсем много времени будет, то про random, но нет)
Д/З
- Прочитать:
в Tutorial про функции
Про замыкания: Gabor Laszlo Hajba и Dmitry Soshnikov TODO
тупая задача на функцию/класс
- задача на рекурсию
- задача на замыкание (если получится)
- непростая задача на функцию