Функции и замыкание

Долги за прошлую лекцию:

Функции

Пространства имён: повторение

Про рекурсию

Замыкание

Замыкание_(программирование)

  1. Функция — это объект
  2. Её можно изготовить внутри другой функции и вернуть
  3. …причём в зависимости от параметров этой другой функции!
  4. …в процессе чего некоторые объекты из ПИ создающей функции «залипают» в ПИ создаваемой
    • только они там навсегда должны залипнуть, а не только на время вызова
    • .__closure__

  5. Это и есть замыкание!

Пример:

   1 def f1(x):
   2     def f2():
   3         return x
   4     return f2

pythontutor this

и

   1 def f1(x):
   2     def f2():
   3         def f3():
   4             return x
   5         return f3
   6     return f2

pythontutor this

Also: nonlocal name — явное указание брать имя name из внешнего, но не глобального пространства имён

Примеры: 1 и 2

Модификатор nonlocal для доступа к пространству имён вызывающей функции:

   1 def f(x):
   2     def g(y):
   3         nonlocal x
   4         x = 2 * y + 1
   5         return x
   6     g(2)
   7     return x

Замыкание и позднее связывание

Вот этот код не работает так, как может показаться:

   1 def create_adders():
   2     adders = []
   3     for i in range(10):
   4         def adder(x):
   5             return i + x
   6         adders.append(adder)
   7     return adders
   8 
   9 for adder in create_adders():
  10     print(adder(1))

Обратите внимание на то, что все adder-ы работают одинаково!. Поскольку 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 именовало новый объект:

   1 def create_adders():
   2     adders = []
   3     for i in range(10):
   4         def adder(x, j=i):
   5             return j + x
   6         adders.append(adder)
   7     return adders

При этом никакого замыкания не произойдёт, у каждого adder-а будет своё локальное j, инициализированное соответствующим значением i. (Если бы нам нужно было сильнее запутаться, мы могли бы написать i=i вместо j=i ☺ ).

   1 >>> c = create_adders()
   2 >>> c[1].__closure__
   3 >>> print(c[1].__closure__)
   4 None

Д/З

  1. Прочитать:
  2. (задача типа «написать функцию»)

    EJudge: MoarTuple 'Подсчёт кратных'

    Написать функцию moar(a, b, n) от трёх параметров — целочисленных последовательностей a и b, и натурального числа n. Функция возвращает True, если в a больше чисел, кратных n, чем в b, и False в противном случае.

    Input:

    print(moar((25,0,-115,976,100500,7),(32,5,78,98,10,9,42),5))
    Output:

    True
  3. (задача типа «написать функцию»)

    EJudge: MaxFun 'Функция побольше'

    Написать функцию maxfun(), которая принимает переменное число параметров — числовую последовательность S, функцию F1 и, возможно, ещё несколько функций F2 … Fn. Возвращает она ту из функций Fi, сумма значений которой на всех элементах S наибольшая. Если таких функций больше одной, возвращается Fi с наибольшим i.

    Input:

    from math import *
    print(maxfun(range(-2,10), sin, cos, exp)(1))
    Output:

    2.718281828459045
  4. (задача типа «написать функцию»)

    EJudge: Without2Zeros 'Без двух нулей'

    Написать функцию No_2Zero(N, K), которая вычисляет количество N-значных чисел в системе счисления с основанием K, таких что их запись не содержит двух подряд идущих нулей. Лидирующие нули не допускаются. Для EJudge N⩽33.

    Input:

    print(No_2Zero(6, 3))
    Output:

    328
  5. (задача типа «написать функцию»)

    EJudge: ArithFunct 'Арифметика функций'

    Написать четыре функции (функционала): ADD(f, g), SUB(f, g), MUL(f, g) и DIV(f, g), параметрами которых могут быть как обычные объекты, так и функции от одной переменной (проверить, является ли объект функцией можно с помощью callable(объект)). Возвращать эти функционалы должны функцию от одной переменной h(x), которая выполняет соответствующее действие — f(x)+g(x), f(x)-g(x), f(x)*g(x) или f(x)/g(x) — над этими переменными. Если f или g не были функцией, вместо f(x) используется f, а вместо g(x)g (например, при умножении функции на константу).

    Input:

    from math import *
    
    f = SUB(sin, cos)
    print(f(12), sin(12)-cos(12))
    
    g = DIV(sin, cos)
    print(g(pi/6), tan(pi/6))
    
    h = MUL(exp, 0.1)
    print(h(2), e**2/10)
    
    t = ADD(len, sum)
    print(t(range(5)))
    Output:

    -1.380426876732927 -1.380426876732927
    0.5773502691896256 0.5773502691896257
    0.7389056098930651 0.738905609893065
    15

LecturesCMC/PythonIntro2022/04_FunctionsClosure (последним исправлял пользователь FrBrGeorge 2022-10-09 13:49:39)