Введение в язык Питон

         

Присвоение значений


Внимательный читатель помнит об упоминаемом мной ограничении функциональной техники, описанной в предыдущей статье. Речь шла о том, что ничто в Python не запрещает переприсваивания другого значения имени, ссылающемуся на функциональное выражение. В ФП под именами понимается всего лишь буквенное сокращение более длинных выражений, при этом подразумевается, что одно и то же выражение всегда приводит к одному и тому же результату . Если же уже определенному имени присваивается новое значение, это допущение нарушается. Предположим, мы определяем неколько сокращений для использования в нашей функциональной программе, как в следующем примере:

    #---- FP-сессия Python с переприсваиванием приводит к неприятностям ---#

    >>> car = lambda lst: lst[0]

    >>> cdr = lambda lst: lst[1:]

    >>> sum2 = lambda lst: car(lst)+car(cdr(lst))

    >>> sum2(range(10)) 1

    >>> car = lambda lst: lst[2]

    >>> sum2(range(10)) 5

К несчастью, одно и то же выражение sum2(range(10))

вычисляется к разным результатам в двух местах программы, несмотря на то, что аргументы выражении не являются изменяемыми переменными.

К счастью, модуль functional

предоставляет класс Bindings (предложенный Келлеру автором), предотвращающий такое переприсваивание (по крайней мере, случайное; Python не препятствует решительному программисту намеренно нарушать правила). Хотя использование Bindings

требует немного дополнительного кода, это оградит вас от случайностей. Келлер обозначает экземпляр класса Bindings как let

(я полагаю, из-за зарезервированного слова let

в ML-языках программирования).



Например, мы могли бы сделать следующее:

    #------- FP-сессия Python с защитой от переприсваивания -------#

    >>> from functional import *

    >>> let = Bindings()

    >>> let.car = lambda lst: lst[0]

    >>> let.car = lambda lst: lst[2]

    Traceback (innermost last):

    File "", line 1, in ?

             File "d:\tools\functional.py", line 976, in __setattr__



               raise BindingError, "Binding '%s' cannot be modified." % name

    functional.BindingError:  Binding 'car' cannot be modified.

    >>> let.car(range(10)) 0



Разумеется, реальная программа должна перехватить и обработать исключение BindingError, однако сам факт его возбуждения позволяет избежать целого класса проблем.

Помимо класса Bindings, functional

содержит функцию namespace, предоставлюющую доступ к пространству имен (на самом деле, к словарю) из экземпляра класса Bindings. Это очень удобно, если вам нужно вычислить выражение в (неизменяемом) пространстве имен, определенном в Bindings. Функция eval() в Python позволяет проводить вычисление в пространстве имен. Следующий пример поясняет сказанное:



    #----- FP-сессия Python, использующая неизменяемые пространства имен -----#

    >>> let = Bindings()      # "Real world" function names

    >>> let.r10 = range(10)

    >>> let.car = lambda lst: lst[0]

    >>> let.cdr = lambda lst: lst[1:]

    >>> eval('car(r10)+car(cdr(r10))', namespace(let))

    >>> inv = Bindings()      # "Inverted list" function names

    >>> inv.r10 = let.r10

    >>> inv.car = lambda lst: lst[-1]

    >>> inv.cdr = lambda lst: lst[:-1]

    >>> eval('car(r10)+car(cdr(r10))', namespace(inv))

    17



Содержание раздела