Параметрические итераторы

Множества и словари (практическая часть)

TODO

Параметрические итераторы

В итератор можно затолкать значение на каждом обороте (оно прочтётся yield-ом).

   1 >>> def biased(init):
   2 ...     bias = yield init
   3 ...     while bias:
   4 ...         init += bias*2+1
   5 ...         bias = yield init
   6 ... 
   7 >>> g = biased(10)
   8 >>> next(g)
   9 10
  10 >>> g.send(5)
  11 21
  12 >>> g.send(5)
  13 32
  14 >>> g.send(-1)
  15 31
  16 >>> g.send(100500)
  17 201032
  18 >>> g.send(0)
  19 Traceback (most recent call last):
  20   File "<stdin>", line 1, in <module>
  21 StopIteration

Декораторы

Что, если мы хотим «обмазать» все вызовы некоторой функции отладочной информацией?

   1 def fun(a,b):
   2     return a*2+b
   3 
   4 def dfun(f, *args):
   5     print(">", *args)
   6     res = f(*args)
   7     print("<", res)
   8     return res
   9 
  10 
  11 print(fun(2,3))
  12 print(dfun(fun,2,3))

Неудобно! Поиск с заменой fun(a,b) на dfun(fun,a,b).

Создадим обёрнутую функцию вместо старой:

   1 # ...
   2 def genf(f):
   3     def newfun(*args):
   4         print(">", *args)
   5         res = f(*args)
   6         print("<", res)
   7         return res
   8     return newfun
   9 
  10 newf = genf(fun)
  11 print(newf(2,3))

Всё равно поиск с заменой, хотя и попроще. Тогда просто перебьём имя fun!

   1 # ...
   2 fun = genf(fun)
   3 print(fun(2,3))

Вот это и есть декоратор, записывается так:

   1 def genf(f):
   2     def newfun(*args):
   3         print(">", *args)
   4         res = f(*args)
   5         print("<", res)
   6         return res
   7     return newfun 
   8 
   9 @genf
  10 def fun(a,b):
  11     return a*2+b
  12 
  13 print(fun(2,3))

Закомментировали @genf — убрали декоратор!

(Не успели — параметрические декораторы)

Замыкание

Что не так в этом коде?

   1 def addN(c):
   2     def fun(n):
   3         return n + c
   4     return fun
   5 
   6 add1 = addN(1)
   7 print(add1(100500))
   8 add10 = addN(10)
   9 print(add10(100500))

А то не так, что это вот «c» функция fun() берёт не из глобального пространства имён, а из объемлющего. И оно там залипает, несмотря на то, что должно было бы удалиться при выходе из функции addN(). Такие имена помещаются в замыкание (closure):

   1 >>> add1.__closure__
   2 (<cell at 0x7ff7cc5e6d98: int object at 0x7ff7cd9df4a0>,)
   3 >>> cc = 20
   4 >>> id(cc)
   5 140702283331328
   6 >>> add20=addN(cc)
   7 >>> add20.__closure__
   8 (<cell at 0x7ff7cc33c2e8: int object at 0x7ff7cd9df700>,)
   9 >>> 0x7ff7cd9df700
  10 140702283331328
  11 

Т. е. содержимое таинственной «ячейки» — это как раз объект 20.

Д/З

  1. TODO где почитать про декораторы и параметрические итераторы?

  2. EJudge: ToInt 'Вещественные в целые'

    Написать функцию-декоратор toint(function), которая модифицирует произвольную (для простоты — не имеющую именных параметров) функцию, заменяя с помощью int() все её вещественные параметры, а также возвращаемое значение, целыми. Параметры и/или возвращаемое значение другого типа не меняются.

    Input:

       1 @toint
       2 def root(x):
       3     return x**0.5
       4 
       5 print(root(11.8))
    
    Output:

    3
  3. EJudge: VirtualTurtle 'Примитивная черепашка'

    Написать параметрический итератор turtle(coord, direction), описывающий движение «черепахи» по координатной плоскости. coord — это кортеж из двух целочисленных начальных координат, direction описывает первоначальное направление (0 — восток, 1 — север, 2 — запад, 3 — юг). Координаты увеличиваются на северо-восток. Итератор принимает три команды — "f" (переход на 1 шаг вперёд), "l" (поворот против часовой стрелки на 90°) и "r" (поворот по часовой стрелке на 90°) и возвращает текущие координаты черепахи.

    Input:

       1 robo = turtle((0,0),0)
       2 start = next(robo)
       3 for c in "flfrffrffr":
       4     print(*robo.send(c))
    
    Output:

    1 0
    1 0
    1 1
    1 1
    2 1
    3 1
    3 1
    3 0
    3 -1
    3 -1
  4. EJudge: DoPolIZ 'Обратная польская запись'

    (Чуть усложнённая MCCME). В постфиксной записи (или обратной польской записи) операция записывается после двух операндов. Например, сумма двух чисел A и B записывается как A B +. Запись B C + D * обозначает привычное нам (B + C) * D, а запись A B C + D * + означает A + (B + C) * D. Дано выражение в постфиксной записи, содержащее целые десятичные числа, операции +, и *, а также слова, не являющиеся целыми числами и вышеуказанными операциями. Удалите все незнакомые слова из строки и вычислите значение полученного выражения (вещественные числа также не являются целыми, их тоже надо удалять). Стек при вычислении выражения гарантированно заполнен, но не обязательно до конца опустошается (в примере ниже он содержит два значения).

    Input:

    100500 8 33e 9 + @ -44f4 -- 1 1.1 7 - * -10 -
    Output:

    -92

LecturesCMC/PythonIntro2018/08_DictsIters (последним исправлял пользователь FrBrGeorge 2018-11-28 01:22:16)