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

Во-первых, давайте рассмотрим «лямбда-функции».

Лямбда-функции

До сих пор мы всегда использовали def для определения функций в языке программирования Python. В этом разделе мы поговорим о новом выражении, похожем на def, которое позволяет нам определять функции на языке программирования Python. Это новое выражение, которое позволяет нам определять функции, называется лямбда. Функции, созданные с помощью этого выражения, называются лямбда-функциями:

Как вы знаете, в Python мы определяем функцию с помощью оператора def следующим образом:

def function(param_one, param_two):
    returned_value = param_one + param_two # do something
    return returned_value

Что, если мы хотим сделать то же самое с лямбда-функциями?

Смотри внимательно:

function = lambda param_one, param_two: param_one + param_two

Здесь мы определили лямбда-функцию. Мы можем использовать эту лямбда-функцию точно так же, как функцию def, которую мы только что определили:

>>> function(5, 7)
>>> 12

Как видите, лямбда-функции несложно определить и использовать. Теперь, когда мы рассмотрели основы того, как выглядят лямбда-функции, мы можем копнуть немного глубже.

Лямбда-функции — это одна из продвинутых функций языка программирования Python. С помощью приведенного выше примера мы увидели, как выглядят эти лямбда-функции. Фактически, мы часто используем лямбда-функции в приложениях, подобных приведенному ниже.

letters = "abcçdefgğhıijklmnoöprsştuüvyz"
dict_letter = {i: letters.index(i) for i in letters}

names = ["jack", "john", "micheal", "sonia",
           "alex", "david", "dario"]

print(sorted(names, key=lambda x: dict_letter.get(x[0])))

Здесь выражение, которое мы используем в ключевом параметре sorted(), является лямбда-функцией.

Так что же такое лямбда-функции и что они делают?

Мы можем использовать лямбда-функции в ситуациях, когда нам нужна функциональность функции, но мы не можем определить функцию по местоположению или когда определение функции сложно или утомительно. Приведенный выше пример кода является хорошим примером этого: ключевой параметр sorted() ожидает определения функции. Но, конечно же, мы не можем напрямую определить функцию с помощью def. Но мы можем определить лямбда-функцию для ключевого параметра, используя lambda вместо def.

Если бы мы хотели написать приведенный выше код с «нормальной» функцией, мы могли бы использовать следующий код:

letters = "abcçdefgğhıijklmnoöprsştuüvyz"
dict_letter = {i: letters.index(i) for i in letters}

names = ["jack", "john", "micheal", "sonia",
           "alex", "david", "dario"]

def sorting(eleman):
    return dict_letter.get(eleman[0])

print(sorted(names, key=sorting))

Здесь вместо использования лямбда-функции мы использовали функцию sorting().

Если вы сравните пример, который мы написали выше с «лямбда», с примером, который мы написали с функцией sorting(), вы можете легко понять, какая часть соответствует тому, что или что это означает в лямбда-функциях.

Допустим, мы хотим вычислить квадрат всех чисел в списке. Вот список, который у нас есть:

l = [5, 10, 15, 20, 25]

Используя lambda с функцией map(), мы можем написать следующий код:

print(*map(lambda sayı: sayı ** 2, l))
>>> 25 100 225 400 625

В двух словах, например, этот код:

lambda x: x + 1

Это означает, что нужно определить лямбда-функцию, которая принимает параметр с именем «x». Эта функция принимает это, добавьте число 1 к параметру x.

Как видно из этих примеров, лямбда-функции — очень практичные инструменты. С помощью лямбда-функций мы можем получить ту же функциональность, что и с обычными именованными функциями, но за гораздо более короткое время. Однако лямбда-функции немного более неразборчивы, чем обычные функции. Итак, если вам не нужно использовать лямбда-функции, вы можете вместо этого использовать обычные функции или генераторы списков.

Рекурсивные функции

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

Итак, приступим сразу…

До сих пор мы видели много функций в Python. Иногда эти функции являются «встроенными функциями», определенными программистами Python и интегрированными в язык, а иногда это «пользовательские функции», которые мы определяем сами в соответствии с нашей текущей ситуацией и потребностями.

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

def greetings(who):
    print('Hey!', who)

Здесь мы определили функцию с именем Greetings(). Как видите, эта функция вызывает другую функцию с именем print(). Здесь нет ничего необычного. Как мы уже говорили, мы видели такие функции все время.

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

Возьмем очень простой пример. Допустим, вы хотите написать функцию, которая выводит на экран строку символов, заданных в качестве параметров, уменьшая их один за другим. Так, например, у вас есть строка под названием «coding-python». Ваша цель — написать функцию, которая печатает эту строку следующим образом:

coding-python
oding-python
ding-python
ing-python
ng-python
g-python
-python
python
ython
thon
hon
on
n

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

Теперь внимательно посмотрите на эти коды:

def decrease(string):
    if len(string) < 1:
        return string
    else:
        print(string)
        return decrease(string[1:])

print(decrease('coding-python'))

Эти коды дадут нам вывод, упомянутый выше.

Теперь, когда мы написали и выполнили нашу функцию и увидели, какой результат она нам дает, мы можем перейти к объяснению функции.

На первый взгляд, эта функция не сильно отличается от функций, которые мы изучали ранее. Но если вы посмотрите на последнюю часть функции, то увидите следующую строку, которая отличает эту функцию от функций, которые мы изучили ранее:

return decrease(string[1:])

Как вы можете видеть, мы снова вызываем функцию уменьшения() внутри функции уменьшения(). Таким образом, наша функция постоянно повторяет себя. Другими словами, он применяет одну и ту же функцию снова и снова.

Рекурсивные функции — это функции, которые позволяют нам решать большую проблему, работая с меньшей частью проблемы, представляющей всю проблему. Мы применяем этот принцип в приведенном выше примере.

Вложенные функции

В этом разделе мы рассмотрим, что такое вложенные функции и как их можно использовать. Как следует из названия, когда у нас есть несколько вложенных функций, мы называем их вложенными функциями. Давайте представим, что у нас есть две функции, как показано ниже:

def f1():
    def f2():
        ......

Здесь f1() — это объемлющая или внешняя функция, а f2() — наша вложенная или внутренняя функция. Можно сказать, что наши вложенные функции обладают интересными свойствами. Кроме того, хорошее понимание этих функций поможет нам лучше понять генераторы в будущем.

Лучший способ понять вложенные функции — рассмотреть пример. Давайте определим такую ​​функцию:

def printer():
    def print_it(m):
        print(m)
    return print_it

y = printer()
y("Ciao")
print(type(y))
print(y)

>>> Ciao
>>> <class 'function'>
>>> <function printer.<locals>.print_it at 0x7f979819c3a0>

Теперь давайте рассмотрим этот вывод. Когда вызывается наша функция принтера, она возвращает функцию print_it в качестве значения. Поскольку эта функция print_it определена внутри нашей функции принтера, она является нашей внутренней функцией. принтер — это наша функция-контейнер. Когда вызывается команда y("Ciao"), Ciao записывается на экране. Потому что это то, что делает функция print_it, которая является значением, присвоенным y. Если вы обратите внимание, то увидите, что тип y также является функцией. В нашем последнем выводе мы видим необычное выражение «местные жители». Мы немного поговорим об этом сейчас.

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

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

a = printer()
print(a)
b = printer()
print(b)

>>> <function printer.<locals>.print_it at 0x7ff7402448b0>
>>> <function printer.<locals>.print_it at 0x7ff740244940>

Это означает, что наша переменная y принадлежит предыдущему вызову функции принтера, т.е. это функция записи, определенная внутри нее. locals означает локальные переменные. Таким образом, функция print_it здесь — это локальная переменная, определенная внутри функции принтера, которую мы вызвали ранее. Выше мы видим, что каждая определенная функция принтера отличается.

Использование вложенных функций

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

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

Вложенные функции могут сделать некоторые операции более эффективными, но они также могут замедлить некоторые операции (если мы используем их неправильно или без необходимости).

Например, давайте посмотрим на эту функцию:

def do_operation(number, divisor, *additions):
    result = number / divisor
    for i in additions:
        result += i
    return result

После деления числового параметра на параметр делителя эта функция добавляет к результату все остальные параметры и возвращает его. Мы уже узнали, что означает *дополнения, теперь давайте воспользуемся этой функцией:

do_operation(12, 2, 6, 8)

>>> 20.0

Теперь допустим, что в программе, которую мы пишем, мы будем использовать разные параметры числа и делителя и всегда одни и те же параметры сложения. Итак, мы будем делать такие операции:

do_operation(4, 2, 1, 4, 5)
>>> 12.0
do_operation(60, 12, 1, 4, 5)
>>> 15.0
do_operation(48, 4, 1, 4, 5)
>>> 22.0
do_operation(12, 6, 3, 6, 2)
>>> 13.0
do_operation(12, 4, 3, 6, 2)
>>> 14.0
do_operation(105, 15, 3, 6, 2)
>>> 18.0

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

def operation_constructor(*additions):
    add = 0
    for i in additions:
        add += i

    def operation(number, divisor):
        return number/divisor + add

    return operation

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

processor = operation_constructor(1, 4, 5)
processor2 = operation_constructor(3, 6, 2)
processor(4, 2)
>>> 12.0
processor(60, 12)
>>> 15.0
processor(48, 4)
>>> 22.0
processor2(12, 6)
>>> 13.0
processor2(12, 4)
>>> 14.0
processor2(105, 15)
>>> 18.0

Теперь мы создадим функцию, которая сделает нужную операцию только один раз и будет использовать ее все время. Это предотвращает постоянную отправку одних и тех же параметров функции в качестве параметров и гарантирует, что необходимые операции выполняются только один раз. Когда вы создаете функцию для каких-либо целей в своем собственном коде и сравниваете ее с подобной ситуацией при использовании этой функции, я думаю, у вас теперь есть представление о том, как сделать код более эффективным с помощью вложенных функций.

Генераторы

Генераторы определяются аналогично функциям. Можно даже сказать, что единственное отличие состоит в выражении yield. Если вы помните, мы несколько раз упоминали генераторы в теме вложенных функций. Итак, я хотел бы начать со сравнения вложенной функции и генератора, который будет выполнять ту же работу:

def function_counter():
    count = 0
    def count_function():
        nonlocal count
        count += 1
        return count
    return count_function

def generator_counter():
    count = 0
    while True:
        count += 1
        yield count

Не волнуйся. В дальнейшем мы проанализируем, как работает генератор_счетчика. А пока давайте просто сосредоточимся здесь:

type(function_counter)
<class 'function'>
>>> type(generator_counter)
<class 'function'>

>>> n_function= function_counter()
>>> generator = generator_counter()

>>> type(n_function)
<class 'function'>
>>> type(generator)
<class 'generator'>

>>> n_function()
1
>>> n_function()
2
>>> n_function()
3
>>> n_function()
4

>>> next(generator)
1
>>> next(generator)
2
>>> next(generator)
3
>>> next(generator)
4

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

Мы уже видели, как работает n_function, в разделе о вложенных функциях. Теперь давайте поговорим о следующей функции и операторе yield. Во-первых, следующая функция является встроенной. Чтобы понять, что он делает, нам нужно понять оператор yield. Если вы посмотрите на наш код и результат, вы заметите, что инструкция yield в некотором роде похожа на инструкцию return. Но есть и важные отличия. Прежде всего, как вы заметили, оператор yield определяет, какое значение следует вернуть. Так чем же это отличается от возврата значения с помощью return? Когда в функции достигается оператор return, функция завершается, а локальные переменные функции удаляются. Это не относится к оператору yield. Как и во вложенных функциях, где внутренняя функция использует переменную внешней функции, локальные переменные генераторов хранятся в Python. Однако генераторы хранят не определенные переменные, а все локальные переменные. Генераторы дают нам более полезную форму алгоритма с использованием выражения yield.

Мы упомянули, что next — это встроенная функция, а оператор yield позволяет нам возвращать значение из нашего генератора. Итак, каковы правила, регулирующие эти операции?

Давайте попробуем объяснить метод yield, определив простой генератор:

def generator():
        yield "Hello"
        yield "World"

g = generator()
next(g)
>>> "Hello"
next(g)
>>> "World"
next(g)

Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    next(g)
StopIteration

Если мы изучим наш вывод, то увидим, что следующая функция выполняет код данного генератора до тех пор, пока не встретит оператор yield, а когда встретит оператор yield, она вернет переменную справа от оператора. Обратите внимание, что это следующая функция, которая возвращает это значение. Когда в нашем генераторе не осталось инструкций, наша следующая функция вызывает ошибку StopIteration.

Термин «итерация» используется для того, что здесь делает «следующая» функция. Объекты, которые могут быть переданы в качестве параметров «следующей» функции, являются «итерируемыми объектами». Класс «генератор» является примером итерируемого объекта.

Краткое содержание

В этой статье мы рассмотрели дополнительные темы о функциях в языке программирования Python. Основными предметами были:

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

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

Вложенные функции.
Вложенные функции — это несколько вложенных функций. Вложенные функции обладают интересными свойствами и могут помочь нам лучше понять генераторы в будущем. Их часто используют декораторы.

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

Спасибо, что нашли время, чтобы прочитать его. До встречи в следующих статьях.

Если у вас есть какие-либо вопросы или вы хотите связаться со мной, все мои аккаунты в социальных сетях находятся по ссылке ниже.



Вы также можете следить за другими моими сообщениями в блоге на моем веб-сайте ниже.