Pull to refresh

Откуда идут «функциональные» корни Python

Reading time3 min
Views4.2K
Original author: Guido van Rossum
Я никогда не полагал, что Python попадет под влияние функциональных языков, независимо от того что люди говорят или думают. Я знаком с императивными языками, такими как C и Algol68 и хотя я сделал функции объектами «первого класса», я не рассматривал Python как язык функционального программирования. Однако, было ясно, что пользователи хотят больше от списков и функций.

Операции над списками применялись к каждому элементу списка и создавали новый список. Например:

def square(x):
    return x*x

vals = [1, 2, 3, 4]
newvals = []
for v in vals:
    newvals.append(square(v))

На функциональных языках, таких как Lisp и Scheme, операции, такие как эта были разработаны как встроенные функции языка. Таким образом пользователи, знакомые с такими языками, реализовывали аналогичную функциональность в Python. Например:

def map(f, s):
    result = []
    for x in s:
            result.append(f(x))
    return result

def square(x):
    return x*x

vals = [1, 2, 3, 4]
newvals = map(square,vals)

Тонкость вышеупомянутого кода в том, что многим людям не нравился факт, что функция применяется к элементам списка как набор отдельных функций. Языки, такие как Lisp позволили функциям определяться «налету» при маппинге. Например, в Scheme Вы можете создавать анонимные функции и выполнить маппинг в единственном выражении, используя лямбду, как ниже:

(map (lambda (x) (* x x)) '(1 2 3 4)) 

Хотя в Python функции были объектами «первого класса», у него не было никакого подобного механизма для того, чтобы создавать анонимные функции.

В конце 1993, пользователи метались вокруг различных идей, чтобы создать анонимные функции. Например, Марк Луц написал код функции, которая создает функции, используя exec:

def genfunc(args, expr):
    exec('def f(' + args + '): return ' + expr)
    return eval('f')

# Sample usage
vals = [1, 2, 3, 4]
newvals = map(genfunc('x', 'x*x'), vals)

Тим Петерс написал решениерешения, которое упростило синтаксис, разрешая пользователям следующее:

vals = [1, 2, 3, 4]
newvals = map(func('x: x*x'), vals)


Стало ясно, что действительно появилось необходимость в такой функциональности. Одновременно, это казалось заманчиво — определить анонимные функции как строки кода, которые программист должен вручную обработать через exec. Таким образом, в январе 1994, map(), filter(), и reduce() функции были добавлены к стандартной библиотеке. Кроме того, был представлен оператор лямбды для того, чтобы создавать анонимные функции (как выражения) в более прямом синтаксисе. Например:

vals = [1, 2, 3, 4]
newvals = map(lambda x:x*x, vals)

Эти дополнения были существенными на ранней стадии разработки. К сожалению, я не помню автора, и логи SVN не записали его. Если это Ваша заслуга, оставьте комментарий!

Мне никогда не нравилось использовать терминологию «лямбды», но из-за отсутствия лучшей и очевидной альтернативы, она была принята в Python. В конце концов это был выбор теперь анонимного автора, и в то время большие изменения требовали намного меньшего количества обсуждений чем в настоящее время.

Лямбда была предназначена действительно только, чтобы быть синтаксическим сахаром для определения анонимных функций. Однако, у такого выбора терминологии было много непреднамеренных последствий. Например, пользователи, знакомые с функциональными языками, ожидали, что семантика лямбды будет соответствовать онной из других языков. В результате они нашли, что реализации Python очень недоставало расширенного функционала. Например, проблема с лямбдой состоит в том, что предоставленное выражение не могло обратиться к переменным в окружающем контексте. Например, если бы у Вас этот код, map() прервется, потому что функция лямбды работала бы с неопределенной ссылкой на переменную 'a'.

def spam(s):
    a = 4
    r = map(lambda x: a*x, s)

Существовали обходные решения этой проблемы, но они заключались в использовании «аргументов по умолчанию» и передачу скрытых параметров в лямбда-выражение. Например:

def spam(s):
    a = 4
    r = map(lambda x, a=a: a*x, s)


«Корректное» решение этой проблемы для внутренних функций было в том, чтобы неявно перенести ссылки на все локальные переменные в контекст функции. Данный подход известен как «замыкание» и является важным аспектом функциональных языков. Однако, эта возможность не была представлена в Python до выпуска версии 2.2.

Любопытно, что map, filter, и reduce функции, которые первоначально смотивировали введение лямбды, и других функциональных возможностей были в большой степени заменены на “list comprehensions” и генераторы. Фактически, функция reduce была удалена из списка встроенных функций в Python 3.0.

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

Наконец, даже при том, что много возможностей функционального программирования были введены за эти годы, Python все еще испытывает недостаток в определенном функционале из «настоящих» функциональных языков. Например, Python не выполняет определенные виды оптимизации (например, хвостовая рекурсия). Динамический характер Python сделал невозможным ввести оптимизации времени компиляции, известные в функциональных языкых как Haskell или ML.
Tags:
Hubs:
+46
Comments35

Articles

Change theme settings