Эта статья возникла из личного публичного аккаунта:TechFlow, оригинальность это не просто, прошу внимания
СегодняЧасть 9 тем PythonВ этой статье поговорим о функциональном программировании и замыканиях в Python.
функциональное программирование
функциональное программированиеВозможно, мы слышали об этой концепции более или менее, но когда мы впервые услышали о ней, мы не осознавали этого и думали, что это очень сложная концепция. Но на самом деле его значение очень простое, но оно расширяет множество богатых применений.
В первые дни, когда языков программирования было немного, мы делили языки наЯзыки высокого и низкого уровня. Например, ассемблер — это низкоуровневый язык почти без инкапсуляции, и чтобы выполнить операцию присваивания, нам нужно вручную вызвать регистры. Язык высокого уровня извлекается из этих машинно-ориентированных инструкций и превращается в процессный или объектно-ориентированный. То есть мы пишем код для вычислительного процесса или объекта, абстрагированного от компьютера. Если вы изучали объектно-ориентированный подход, то обнаружите, что по сравнению с процессно-ориентированным объектно-ориентированный язык имеет более высокую степень абстракции и более полную инкапсуляцию.
После объектно-ориентированного, что мы можем сделать с инкапсуляцией и абстракцией? Настала очередь функционального программирования.
Все мы знаем, что функция — это программа, которую мы определяем, и ее вход и выход определены. Пишем функцию, которую можно вызывать где угодно. Поскольку эта функция очень проста в использовании, можно лиДумайте о функции как о переменной для возврата и передачи параметровШерстяная ткань?
Хорошо, это самая интуитивная функция функционального программирования. Другими словами, функция, которую мы пишем, также может использоваться как переменная, которую можно использовать для присваивания, передачи и возврата. Таким образом, наше кодирование значительно облегчается, но это не выгодно и не безвредно, а, наоборот, приносит много проблем, наиболее интуитивно понятная проблема заключается в том, что параметр, передаваемый функцией, может также быть другой функцией, что вызовет множество проблем. проблемы.Делает процесс вычисления функции неопределимым, вероятно, произойдет много вещей, превосходящих наши ожидания.
Таким образом, функциональное программирование имеет свои преимущества и недостатки, оно упрощает многие проблемы, но также создает много новых проблем, и нам нужно быть осторожными в процессе его использования.
Передать и вернуть функции
Когда мы представили фильтр, карту, сокращение и пользовательскую сортировку раньше, мы фактически использовали концепцию функционального программирования.
Например, когда мы вызываем sorted для сортировки, если мы передаем массив объектов, мы хотим отсортировать в соответствии с сформулированными нами полями.В настоящее время нам часто нужно передавать массив объектов.анонимная функция, который используется для указания поля сортировки. На самом деле переданная анонимная функция является наиболее интуитивным воплощением функционального программирования:
sorted(kids, key=lambda x: x['score'])
Кроме того, мы также можем вернуть функцию, например, давайте рассмотрим пример:
def delay_sum(nums):
def sum():
s = 0
for i in nums:
s += i
return s
return sum
Если мы вызовем delay_sum в этот момент и передадим строку чисел, что мы получим?
Ответ представляет собой функцию, мы можем вывести его напрямую, мы можем видеть это из печатной информации:
>>> delay_sum([1, 3, 4, 2])
<function delay_sum.<locals>.sum at 0x1018659e0>
Что мы должны сделать, чтобы получить результат этой операции? Это также очень просто, мы используем переменную для ее получения, а затем выполняем эту новую переменную:
>>> f = delay_sum([1, 3, 4, 2])
>>> f()
10
Преимущество этого в том, что мы можемРасчет задержки, если мы не используем функциональное программирование, то нам нужно вычислить результат, когда мы вызываем функцию delay_sum. Это нормально, если объем вычислений небольшой, но если объем вычислений велик, это приведет к накладным расходам. И когда мы вычисляем результат, результат может быть использован не сразу, а может быть использован очень поздно. В этом случае мы возвращаем функцию вместо операции, а затем выполняем результат, когда он действительно понадобится позже, тем самым задерживая операцию. Это также общая идея многих вычислительных фреймворков, таких какspark.
Закрытие
Давайте рассмотрим пример, который мы только что привели.В функции delay_sum только что мы реализовали внутреннюю функцию суммы и вызвали параметры, переданные функцией delay_sum, в этой функции. эта паравнешний объемВнутренняя функция, которая ссылается на переменную, называетсяЗакрытие.
На самом деле эта концепция очень яркая, потому что данные, вызываемые внутри этой функции, закрыты для вызывающей стороны и представляют собой полностью черный ящик, пока мы не посмотрим исходный код, мы не знаем источник данных в нем. Помимо незнания источника, важнее то, что он ссылается на переменную внешней функции, а раз это переменная, значит, она динамическая. То есть мыВы можете изменить эффект закрытия, изменив значение некоторых внешних переменных..
Это слишком многословно, поэтому давайте рассмотрим простой пример. В Python есть функция math.pow, которая вычисляет мощность. Например, если мы хотим вычислить квадрат x, мы должны написать:
math.pow(x, 2)
Но если нам нужно только вычислить квадрат в текущем сценарии, нам будет очень хлопотно каждый раз передавать дополнительные 2. В настоящее время мы используем замыкание, чтобы упростить операцию:
def mypow(num):
def pw(x):
return math.pow(x, num)
return pw
pow2 = mypow(2)
print(pow2(10))
С замыканиями мыисправлена вторая переменная, так что нам нужно использовать pow2 только для достижения функции исходного math.pow(x, 2). Если нам вдруг понадобится вычислить мощность 3 или 4 при изменении спроса, нам нужно только модифицировать входящие параметры mypow, а код модифицировать вообще не нужно.
На самом деле, это также самый большой вариант использования замыканий, мы можемРеализуйте очень гибкую функциональность с замыканиями, и изменить некоторые функции через конфигурацию, вместо того, чтобы писать мертвый код через код. Вы должны знать, что для промышленной области онлайн-код нельзя изменить случайно, особенно клиент, такой как пакет программного обеспечения в магазине Apple или магазине Android, который можно извлечь только тогда, когда пользователь обновляет его вручную. Если есть проблема, ее практически невозможно изменить, и вы можете только ждать, пока пользователь обновит ее вручную. Таким образом, нормальная работа заключается в использовании некоторых гибких функций, таких как замыкания,Измените логику кода, изменив конфигурацию.
В дополнение к этому есть еще одна полезность замыканий.Временные переменные или среда выполнения.
Например, давайте посмотрим на следующий код:
def step(x=0):
x += 5
return x
Это функция без замыкания, сколько бы раз мы ее ни вызывали, ответ будет 5. Результат после выполнения x+=5 не будет сохранен, при выходе из функции временное значение будет отброшено. тогда, если яНадеюсь, что каждый звонок основан на результате последнего звонка, то есть каждый раз, когда мы изменяем операцию, мы можем сохранить ее, а не отбрасывать?
В это время вам нужно использовать замыкания:
def test(x=0):
def step():
nonlocal x
x += 5
return x
return step
t = test()
t()
>>> 5
t()
>>> 10
То есть значение нашего x сохраняется,Каждая модификация будет накапливаться, вместо того, чтобы отбрасывать. Здесь следует отметить одну вещь: мы используем новое ключевое слово под названиемnonlocal, это уникальное ключевое слово в Python3, используемое для объявления того, что текущая переменная x не является локальной переменной, поэтому интерпретатор Python перейдет к глобальной переменной, чтобы найти это x, так что параметр x, переданный в тестовом методе, может быть связанный . Python2 больше официально не обновляется и не рекомендуется.
Поскольку в Python все является объектом, если мыДумайте о функции вне замыкания как о классеНа самом деле разница между замыканием и классом не так уж велика, мы можем даже связать функцию с функцией, возвращаемой замыканием, так что это почти объект. Давайте посмотрим пример:
def student():
name = 'xiaoming'
def stu():
return name
def set_name(value):
nonlocal name
name = value
stu.set_name = set_name
return stu
stu = student()
stu.set_name('xiaohong')
print(stu())
Результатом последней операции является xiaohong, потому что мы вызываем set_name для изменения значения вне замыкания. Это, конечно, возможно, но обычно мы этим не пользуемся. По сравнению с написанием класса метод через замыканиеОперация будет быстрее. Причина относительно скрыта, потому что в замыкании нет указателя на себя, что экономит много доступа к переменным и операциям, поэтому скорость вычислений выше. Но псевдообъект, созданный замыканием,Наследование и деривация не могут быть использованыи другие методы, и это несовместимо с обычным использованием, поэтому мы знаем, что такой метод есть, но он не будет использоваться в реальности.
закрытый карьер
Хотя крышка проста в использовании, легко наступить на яму, если вы не будете осторожны.Вот несколько распространенных ям.
Замыкания не могут напрямую обращаться к внешним переменным
Этот момент мы только что упомянули, в заключении мыНевозможно получить прямой доступ к внешним переменнымДа, он должен быть помечен ключевым словом nonlocal, иначе будет сообщено об ошибке.
def test():
n = 0
def t():
n += 5
return n
return t
Например, он сообщит об ошибке:
Переменные цикла нельзя использовать в замыканиях
Большая проблема с замыканиями заключается в том, чтонельзя использовать переменные цикла, Эта яма спрятана глубоко, потому что чисто по логике кода ее не найти. Другими словами, логически правильный код часто удивляет нас при запуске. Это требует от нас глубокого понимания лежащих в его основе принципов. Например, давайте рассмотрим пример:
def test(x):
fs = []
for i in range(3):
def f():
return x + i
fs.append(f)
return fs
fs = test(3)
for f in fs:
print(f())
В приведенном выше примере мы использовали цикл for для создания 3 замыканий и использовали fs для хранения этих трех замыканий и их возврата. Затем мы получаем эти 3 замыкания, вызывая test, а затем делаем вызов.
Эта логика, кажется, не проблема.Согласно причине, эти три замыкания создаются циклом for, и мы используем переменную цикла i в замыкании. Тогда по нашей задумке окончательный вывод должен быть [3, 4, 5], но, к сожалению, в итоге мы получилиРезультат [5, 5, 5].
Это выглядит странно, но это вовсе не странно, потому чтоПеременная цикла i не устанавливается при создании замыкания. Но когда мы выполняем замыкание, мы переходим к поиску значения, соответствующего этому i. Очевидно, что когда мы запускаем замыкание, цикл был выполнен, и в это время i останавливается на 2. Следовательно, все результаты выполнения этих трех замыканий равны 2+3, что равно 5. Эта яма вызвана логикой, выполняемой замыканием в интерпретаторе Python.Логика, которую мы написали, верна, но она не следует нашей логике, поэтому обратите внимание на этот момент.Если вы его забудете, то хотите пройти Отладка будет трудно узнать.
Суммировать
Хотя у крышки есть некоторые проблемы и ямы на поверхности, она все же наша.Часто используемые расширенные функции Python, а также является основой для многих других расширенных применений. Поэтому нам очень необходимо понять и выучить замыкания, и мы не должны отказываться от еды из-за того, что поперхнулись.
На самом деле, это не просто замыкания, многие высоко абстрактные функции имеют более или менее такие проблемы. Потому что когда мы абстрагируемся, мы безусловно упрощаем код и повышаем гибкость, но в то же время позволяемКривая обучения становится круче, приносит больше того, что нам нужно понять и запомнить. По сути, это тоже компромисс: простые в использовании функции требуют кода, а простые в освоении и использовании функции часто означают, что они относительно жесткие и негибкие. Надо держать в уме этот вопрос, но к счастью может быть сложно понять на первый взгляд, но в целом замыкание относительно простое, думаю для вас это не будет проблемой.
Хорошо, это все для сегодняшней статьи. Если вы чувствуете, что что-то приобрели, пожалуйста, нажмитеследовать или повернутьПришлите, ваше небольшое усилие очень важно для меня.