Различия между версиями 3 и 4
Версия 3 от 2020-09-29 14:24:44
Размер: 7982
Редактор: FrBrGeorge
Комментарий:
Версия 4 от 2020-10-01 00:16:53
Размер: 8047
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 140: Строка 140:
  '''TODO'''
 1. тупая [[LecturesCMC/PythonIntro2019/HomeworkRules#Class|задача на функцию/класс]]
 1. задача на рекурсию
 1. задача на замыкание (если получится)
 1. непростая задача на функцию
  * Посмотреть, [[[LecturesCMC/PythonIntro2019/HomeworkRules#Class|как оформлять задачи типа «написать функцию»]]
 1. <<EJCMC(148, Without2Zeros, Без двух нулей)>>
 1. <<EJCMC(148, ArithFunct, Арифметика функций)>>
 1. <<EJCMC(148, FourSquares, Четыре квадрата)>>

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

Функции

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

  • лямбда-функции (функции-выражения)
  • Задание функции: формальные и фактические параметры, return

  • Duck typing: функция как формализация алгоритма
  • вызов функции как локальное пространство имён

    • globals() и locals()

    • сложный случай определение локальности по связыванию, global

    • nonlocal для вложенных вызовов

  • функция как объект: именование, передача в качестве параметра
  • Распаковка и запаковка параметров
    • функция с произвольным числом параметров
  • Параметры функции по умолчанию (именованные параметры)
    • (to be continued… они теперь не равны, вообще см. полный вид описания функции)

Про рекурсию

  • Рекурсия и цикл. Теория vs. практика. Гвидо, Python и хвостовой вызов

    • ⇒ максимальная глубина рекурсии
    • ⇒ логарифмический критерий уместности рекурсии
  • Замещение рекурсии стеком. Пример: есть ли среди натуральных чисел Seq такие, что в сумме дают S. Рекурсивный вариант.

    •    1 def subsumR(S, Seq, Res=[]):
         2     if sum(Res) == S:
         3         return Res
         4     for i, el in enumerate(Seq):
         5         R = subsumR(S, Seq[i+1:], Res+[el])
         6         if R: return R
         7     return []
      
      S — неизменяемая часть, Seq, Res — изменяемая, значит, их надо сохранять в стек. Рекурсия — это цикл, которые продолжается до тех пор, пока рекурсивные вызовы не кончились.
         1 def subsumS(S, Num):
         2     Stack = [(Num,[])]
         3     while Stack:
         4         Seq, Res = Stack.pop()
         5         if sum(Res) == S:
         6             return Res
         7         Stack.extend([(Seq[i+1:], Res+[el]) for i, el in enumerate(Seq)])
         8     return []
      
      • здесь не тот порядок добавления, для полного соответствия надо в обратном
      • вместо списковой сборки надо использовать генератор, но у нас их ещё не было ☺

Замыкание

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

  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

В частности, позднее связывание:

   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))

Поскольку 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

Числа (попытка №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)

  • Про функцию atan2() — см. Atan2

(если уж совсем много времени будет, то про random, но нет)

Д/З

  1. Прочитать:
  2. EJudge: Without2Zeros 'Без двух нулей'

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

    Input:

    print(No_2Zero(6, 3))
    Output:

    328
  3. 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
  4. EJudge: FourSquares 'Четыре квадрата'

    Известно, что любое натуральное число можно представить в виде суммы не более чем четырех квадратов неотрицательных целых чисел (теорема Лагранжа). Ввести натуральное N⩽100000 и найти для него такие целые неотрицательные x,y,z и t, чтобы x²+y²+z²+t²=N. Вывести все такие четвёрки в следующем формате: x,y,z и t — через пробел, и упорядочены по убыванию, а сами четвёрки — лексикографически по возрастанию (без повторений).

    Input:

    100
    Output:

    5 5 5 5
    7 5 5 1
    7 7 1 1
    8 4 4 2
    8 6 0 0
    9 3 3 1
    10 0 0 0

LecturesCMC/PythonIntro2020/04_FunctionsClosure (последним исправлял пользователь FrBrGeorge 2020-10-06 12:28:46)