Функции Python 3: значение, аргументы, вызов, переменные, списки

Содержание страницы

Функции Python 3 — Краткая шпаргалка

Функции – главный и самый важный способ организации и повторного использования кода в Python. Функций не может быть слишком много. 1 функция — это блок кода, который используется для выполнения одного связанного действия.

В Python есть два инструмента для создания функций: def и lambda.

Сначала рассмотрим def. К lambda функции вернемся позже.

Python предоставляет вам множество встроенных функций, таких как print() и т.д., но вы также можете создавать свои собственные функции. Эти функции называются пользовательскими функциями.

Объявление функции начинается ключевым словом def, а результат возвращается в предложении return:

def my_function(x, y, z=1.5):
    if z > 1:
        return z * (x + y)
    else:
        return z / (x + y)

Ничто не мешает иметь в функции несколько предложений return. Если при выполнении достигнут конец функции, а предложение return не встретилось, то возвращается None.

У функции могут быть позиционные и именованные аргументы.

Именованные аргументы обычно используются для задания значений по умолчанию и необязательных аргументов.

В примере выше:

  • x и y – позиционные аргументы,
  • а z – именованный.

Следующие вызовы функции эквивалентны:

my_function(5, 6, z=0.7)
my_function(3.14, 7, 3.5)

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

Определение функции Python 3

Вы можете задать функции для реализации нужной вам функциональности. Вот простые правила для определения функции в Python.

  • Ключевое слово для определения функции: def, за которым следуют имя функции и круглые скобки () с параметрами.
  • Любые входные параметры или аргументы должны быть помещены в эти круглые скобки.
  • В качестве первой команды может быть необязательная конструкция — строка документации функции (эта часть функции — пояснение зачем функция создана, очень рекомендуется использовать для облегчения понимания кода при работе в команде или при повторном возвращении к коду через длительный промежуток времени).
  • Блок кода в каждой функции начинается с двоеточия : и имеет отступ.
  • Оператор return [выражение] возвращает результат из функции. Оператор return без аргументов аналогичен return None. Функции всегда возвращают значение, хотя бы None.

Синтаксис функции Python

def function_name( parameter1, parameter2 ):
   """function_docstring: функция Python предназначена для расчета ROI"""
   function_suite (algorithms, expressions)
   return [expression]

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

Пример реализации функции

Следующая функция принимает строку в качестве входного параметра и печатает ее на стандартном экране.

def print_me( str ):
   """Выводит на печать переданную строку в эту функцию"""
   print(str)
   return

Вызов функции

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

Как только базовая структура функции создана, вы можете выполнить ее, вызвав ее из другой функции или непосредственно из Python prompt (командной оболочки). Ниже приведен пример вызова функции printme():

# Определение функции
def printme( str ):
    """This prints a passed string into this function"""
    print(str)
    return

# Теперь вы можете вызвать функцию printme
printme("I'm first call to user defined function!")
printme("Again second call to the same function")

Результат:

I'm first call to user defined function!
Again second call to the same function

Аргументы функции

Вы можете вызвать функцию, используя следующие типы аргументов:

  • Обязательные аргументы
  • Ключевые аргументы
  • Аргументы по умолчанию
  • Аргументы переменной длины

Обязательные (позиционные) аргументы

Позиционные аргументы: указываются простым перечислением

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

Чтобы вызвать функцию printme() , вам обязательно нужно передать один аргумент, иначе она выдаст следующую синтаксическую ошибку:

# Определение функции
def printme( str ):
   """This prints a passed string into this function"""
   print(str)
   return

# Теперь вы можете вызвать функцию printme
printme()

Ошибка (в функции Printme не указан аргумент):

Traceback (most recent call last):
  File "C:/Users/User/Desktop/CodePythonFunc.py", line 8, in <module>
    printme()
TypeError: printme() missing 1 required positional argument: 'str'

Правильно указать аргумент так:

# Определение функции
def printme( str ):
   """This prints a passed string into this function"""
   print(str)
   return

# Теперь вы можете вызвать функцию printme
printme("Текстовая переменная")

Ключевые аргументы

Ключевые аргументы: указываются перечислением ключ=значение

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

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

# Определение функции
def printinfo( name, age ):
   "This prints a passed info into this function"
   print("Name: " + name + "|Age: " + str(age))
   return

# Вызов функции printinfo
printinfo( age=23, name="Anton" )
printinfo( name="Alena", age=20 )

Результат:

Name: Anton|Age: 23
Name: Alena|Age: 20

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

Преимущества ключевых аргументов в функциях

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

Аргументы по умолчанию

Аргумент по умолчанию — это аргумент, который принимает значение по умолчанию (задается в описании функции), если при вызове функции аргументу не передается значение.

Следующий пример дает представление об аргументах по умолчанию, он печатает возраст по умолчанию, если он не был передан:

# Определение функции
def printinfo( name, age = 35 ):
   "This prints a passed info into this function"
   print("Name: " + name + "|Age: " + str(age))
   return

# Вызов функции printinfo
printinfo( age=19, name="Семен" )
printinfo( name="Николай" )

Результат:

Name: Семен|Age: 19
Name: Николай|Age: 35

Аргументы переменной длины

Передача кортежа в функцию — Использование *args в Python

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

В Python форма с одной звездочкой *argsможет использоваться в качестве параметра для отправки в список функций аргументов переменной длины без ключа. Стоит отметить, что звёздочка ( *) является здесь важным элементом, так как слово argsявляется общепринятой традиционной идиомой, хотя оно не поддерживается языком.

Синтаксис для функции с аргументами переменной длины следующий:

def function_name([formal_args,] *args_tuple ):
    """function_docstring"""
    function_body
    return [expression]

Звездочка (*) ставится перед именем переменной, которая содержит в себе кортеж значений без ключевых слов. Кортеж является необязательным параметром.

Рассмотрим простой пример:

# Определение функции
def print_info( parametr_1, *var_tuple ):
   """Эта функция производит вывод переданных аргументов"""
   print("Output is: ")
   print(parametr_1)
   for elem in var_tuple:
      print(elem)
   return

# Вызов функции printinfo
print_info( 10 )
print_info( 70, 60, 50 )

Результат:

Output is: 
10
Output is: 
70
60
50

Еще один пример использования *args в функции Python:

def multiply(*args):
    z = 1
    for num in args:
        z *= num
    print(z)

multiply(4, 5)
multiply(10, 9)
multiply(2, 3, 4)
multiply(3, 5, 10, 6)

Результат:

20
90
24
900

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

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

Передачи словаря переменной длины с аргументами в функцию **kwargs

**kwargs — сокращение от “keyword arguments”

Форма двойной звездочки **kwargsиспользуется для передачи словарного словаря с аргументами переменной длины в функцию. Опять же, две звездочки ( **) являются здесь важным элементом, так как слово kwargsиспользуется условно, но не поддерживается языком.

Мол *args**kwargsможет принять столько аргументов, которые вы хотели бы привести к этому. Однако **kwargsотличается от того, *argsчто вам нужно будет назначить ключевые слова.

Во-первых, давайте просто распечатаем **kwargsаргументы, которые мы передаем функции. Мы создадим короткую функцию для этого:

def print_kwargs(**kwargs):
        print(kwargs)

print_kwargs(kwargs_1="Shark", kwargs_2=4.5, kwargs_3=True)

Давайте запустим программу выше и посмотрим на вывод:

{'kwargs_1': 'Shark', 'kwargs_2': 4.5, 'kwargs_3': True}

В зависимости от версии Python 3, которую вы используете в данный момент, тип данных словаря может быть неупорядоченным. В Python 3.6 и выше вы получите пары ключ-значение по порядку, но в более ранних версиях пары будут выводиться в случайном порядке.

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

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

def print_values(**kwargs):
    for key, value in kwargs.items():
        print("The value of {} is {}".format(key, value))

print_values(my_name="Sammy", your_name="Casey")

Результат:

The value of my_name is Sammy
The value of your_name is Casey

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

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

Мы также можем использовать *argsи **kwargsдля передачи аргументов в функции.

Сначала давайте рассмотрим пример с *args.

def some_args(arg_1, arg_2, arg_3):
    print("arg_1:", arg_1)
    print("arg_2:", arg_2)
    print("arg_3:", arg_3)

args = ("Sammy", "Casey", "Alex")
some_args(*args)

В функции выше, существует три параметра , определенный как arg_1arg_и arg_3. Функция распечатает каждый из этих аргументов. Затем мы создаем переменную, для которой задано итеративное значение (в данном случае кортеж ), и можем передать эту переменную в функцию с синтаксисом звездочки.

Когда мы запустим код, мы получим следующий вывод:

arg_1: Sammy
arg_2: Casey
arg_3: Alex

Мы также можем изменить вышеприведенную программу для типа данных итеративного списка с другим именем переменной. Давайте также объединим *argsсинтаксис с именованным параметром:

def some_args(arg_1, arg_2, arg_3):
    print("arg_1:", arg_1)
    print("arg_2:", arg_2)
    print("arg_3:", arg_3)

my_list = [2, 3]
some_args(1, *my_list)

Если мы запустим программу выше, она выдаст следующий вывод:

arg_1: 1
arg_2: 2
arg_3: 3

Аналогично, **kwargsаргументы с ключевым словом могут использоваться для вызова функции. Мы установим переменную, равную словарю с 3 парами ключ-значение (мы будем использовать kwargsздесь, но она может называться как угодно), и передадим ее функции с 3 аргументами:

def some_kwargs(kwarg_1, kwarg_2, kwarg_3):
    print("kwarg_1:", kwarg_1)
    print("kwarg_2:", kwarg_2)
    print("kwarg_3:", kwarg_3)

kwargs = {"kwarg_1": "Val", "kwarg_2": "Harper", "kwarg_3": "Remy"}
some_kwargs(**kwargs)

Результат:

kwarg_1: Val
kwarg_2: Harper
kwarg_3: Remy

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

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

Комбинирование параметров *args и **kwargs в функции

При комбинировании параметров параметр с двумя звездочками записывается самым последним. Если в определении функции указывается комбинация параметров с одной звездочкой и двумя звездочками, то функция примет любые переданные ей параметры:

def func(*args, **kwargs):
    """Функция примет любые параметры и выведет их на печать"""
    for elem in args:                   # Перебираем список с переданными параметрами
        print(elem, end=" ")
    for key, value in kwargs.items():   # Перебираем словарь с переданными параметрами
        print("{0} => {1}".format(key, value), end=" ")
    print("") #Перенос строки

func(35, 10, a=1, b=2, c=3) # Выведет: 35 10 a => 1 c => 3 b => 2
func(10)                    # Выведет: 10
func(3, a=1, b=2)           # Выведет: a => 1 b => 2

Результат:

35 10 a => 1 b => 2 c => 3 
10 
3 a => 1 b => 2 

Еще один пример использования комбинации *args & **kwargs:

# Описываем функцию
def echo(*args, **kwargs):
    print(args, kwargs)

# Задаем список
args = (10, 20)
# Задаем словарь
kwargs = {'a':30, 'b':40}

#Вызываем функцию
echo(*args, **kwargs)

Результат:

(10, 20) {'a': 30, 'b': 40}

Порядок аргументов в функции Python

Аргументы внутри вызова функции должны стоять в определенном порядке:

  1. Позиционные аргументы (arg_1, arg_2)
  2. *args
  3. Ключевые аргументы (kw_1=»значение 1″, kw_2=»значение 2″)
  4. **kwargs

Рассмотрим детальный пример с использованием всех функций:

def function_print(arg_1, arg_2, *args, kw_1="значение 1", kw_2="значение 2", **kwargs):
    """ Обобщенный пример вывода всех аргументов, переданных функции """
    print("===Позиционные аргументы===")
    print(str(arg_1) + "," + str(arg_2))
    print("===*args===")
    for elem in args: # Перебираем список с переданными параметрами
        print(elem, end=" ")
    print("") #Перенос строки
    print("===Ключевые аргументы===")
    print(kw_1 + "," + kw_2)
    print("===**kwargs===")
    for key, value in kwargs.items(): # Перебираем словарь с переданными параметрами
        print("{0} => {1}".format(key, value), end=" ")
    print("") #Перенос строки


function_print(1, 2, 4, kw_2="Иван", var1=4)

Результат:

===Позиционные аргументы===
1,2
===*args===
4 
===Ключевые аргументы===
значение 1,Иван
===**kwargs===
var1 => 4 

Передача списка (list) по ссылке в функцию

def myfunction (list):
    list.append(40)
    print ("Modified list inside a function: ", list)
    return

mylist=[10,20,30]
myfunction(mylist)
print(mylist)

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

Modified list inside a function: [10, 20, 30, 40]
[10, 20, 30, 40]

Пространства имен, области видимости и локальные функции

Область действия переменной — область видимости переменных

Переменные в программе могут быть доступны или недоступны в разных местах этой программы. Это зависит от того, где вы объявили переменную.

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

  • Глобальные переменные
  • Локальные переменные

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

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

Переменные внутри функции – локальные. Поиск переменных: сперва среди локальных, потом среди глобальных.

Рассмотрим следующую функцию:

def func():
    a = []
    for i in range(5):
        a.append(i)

При вызове func() создается пустой список a, в него добавляется 5 элементов, а затем, когда функция завершается, список a уничтожается. Но допустим, что мы объявили a следующим образом:

a = []
def func():
    for i in range(5):
        a.append(i)

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

a = None

def bind_a_variable():
    global a
    a = []

bind_a_variable()
print a

Результат:

[]

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

def outer_function(x, y, z):
    def inner_function(a, b, c):
        pass
    pass

Здесь функция inner_function не существует, пока не вызвана функция outer_function. Как только outer_function завершает выполнение, inner_function уничтожается.

Не рекомендуется злоупотреблять ключевым словом global. Обычно глобальные переменные служат для хранения состояния системы. Если вы понимаете, что пользуетесь ими слишком часто, то стоит подумать о переходе к объектно-ориентированному программированию (использовать классы).

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

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

Возврат нескольких значений из одной функции

В Python существует возможность возвращать из функции несколько значений.

Вот простой пример:

def f():
    a = 5
    b = 6
    c = 7
    return a, b, c
var1, var2, var3 = f()

print("var1={0} var2={1} var3={2}".format(var1, var2, var3))

Результат:

var1=5 var2=6 var3=7

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

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

Этот пример можно было бы записать и так:

def f():
    a = 5
    b = 6
    c = 7
    return a, b, c
return_value = f()

print(return_value)

Результат:

(5, 6, 7)

В таком случае return_value было бы кортежем, содержащим все три возвращенные переменные.

Иногда разумнее возвращать несколько значений не в виде кортежа, а в виде словаря:

def f():
    a = 5
    b = 6
    c = 7
    return {'a' : a, 'b' : b, 'c' : c}

return_value = f()

print(return_value)

Результат:

{'a': 5, 'b': 6, 'c': 7}

Функции являются объектами

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

states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']

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

Первая попытка могла бы выглядеть так:

import re # Модуль регулярных выражений

def clean_strings(strings):
    result = []
    for value in strings:
        value = value.strip()
        value = re.sub('[!#?]', '', value) # удалить знаки препинания
        value = value.title()
        result.append(value)
    return result

states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']

states = clean_strings(states)

print(states)

Результат:

['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']

Другой подход, который иногда бывает полезен, – составить список операций, которые необходимо применить к набору строк:

import re # Модуль регулярных выражений

def remove_punctuation(value):
    return re.sub('[!#?]', '', value)

def clean_strings(strings, ops):
    result = []
    for value in strings:
        for function in ops:
            value = function(value)
        result.append(value)
    return result

clean_ops = [str.strip, remove_punctuation, str.title]
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']

states = clean_strings(states, clean_ops)

print(states)

Результат:

['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']

Подобный функциональный подход позволяет задать способ модификации строк на очень высоком уровне. Степень повторной используемости функции clean_strings определенно возросла!

Функции можно передавать в качестве аргументов другим функциям, например встроенной функции map, которая применяет переданную функцию к коллекции:

import re # Модуль регулярных выражений

def remove_punctuation(value):
    return re.sub('[!#?]', '', value)

states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']

states = list(map(remove_punctuation, states))
states = list(map(str.strip, states))
states = list(map(str.title, states))

print(states)

Результат:

['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']

Анонимные функции или Лямбда-функции. Lambda Function Python 3

  • Лямбда-функции позволяют создавать «встроенные» функции, которые полезны для функционального программирования. Например, с Map, Filter или Reduce.
  • Lambda — это инструмент для создания обработчиков обратного вызова (callback handlers).

Lambda Function — это анонимная однострочная функция, которая возвращает всегда 1 результат.

Определяются они с помощью ключевого слова lambda

ИмяФункции = lambda переменная_1, переменная_2, …, переменная_N: <выражение, которое использует переменные>

Например, функция equiv_anon:

equiv_anon = lambda x: x * 2

эквивалентна функции short_function:

def short_function(x):
    return x * 2

Лямбда-функции особенно удобны в ходе анализа данных, потому что, как вы увидите, во многих случаях функции преобразования данных принимают другие функции в качестве аргументов. Часто быстрее (и чище) передать лямбда-функцию, чем писать полноценное объявление функции или даже присваивать лямбда-функцию локальной переменной.

Рассмотрим такой простенький пример:

def apply_to_list(some_list, f):
    return [f(x) for x in some_list]

ints = [4, 0, 1, 5, 6]

result = apply_to_list(ints, lambda x: x * 2)

print(result)

Результат:

[8, 0, 2, 10, 12]

Можно было бы, конечно, написать [x * 2 for x in ints], но в данном случае нам удалось передать функции apply_to_list пользовательский оператор.

Еще пример: пусть требуется отсортировать коллекцию строк по количеству различных букв в строке.

Для этого можно передать лямбда-функцию методу списка sort:

strings = ['foo', 'card', 'bar', 'aaaa', 'abab']

strings.sort(key=lambda x: len(set(list(x))))

print(strings)

Результат:

['aaaa', 'foo', 'abab', 'bar', 'card']

Для чего хороша лямбда-функция? Зачем нужная lambda function в Python?

Ответ:

  • Нам не нужна лямбда функция, мы могли бы обойтись без нее. Но…
  • Есть определенные ситуации, когда это удобно — lambda делает написание кода немного легче, а написанный код — немного чище.

Какие ситуации?

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

Обычно функции создаются для одной из двух целей: (а) для уменьшения дублирования кода или (б) для модульности кода.

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

Но предположим, что вам нужно создать функцию, которая будет использоваться только один раз — вызываться только из одного места в вашем приложении. Ну, во-первых, вам не нужно давать имя функции. Это может быть «анонимно». И вы можете просто определить его прямо там, где вы хотите его использовать. Вот где лямбда полезна.

Еще один пример использования lambda функции:

colors = ["Goldenrod", "Purple", "Salmon", "Turquoise", "Cyan"]

normalized_colors = list(map(lambda s: s.casefold(), colors))

print(normalized_colors)

Результат:

['goldenrod', 'purple', 'salmon', 'turquoise', 'cyan']

Неправильное использование: именование лямбда-выражений

PEP8, официальное руководство по стилю Python, советует никогда не писать такой код:

normalize_case = lambda s: s.casefold()

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

Если вы хотите создать однострочную функцию и сохранить ее в переменной, вы должны использовать defвместо этого:

def normalize_case(s): return s.casefold()

PEP8 рекомендует это, потому что именованные функции — это обычная и понятная вещь. Именованная функция также имеет следующее преимущество: назначая нашей функции правильное имя, Вы упрощаете отладку кода. В отличие от функций, определенных с помощью def, лямбда-функции никогда не имеют имени (это всегда <lambda>).

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

Неправильное использование: ненужные вызовы функций

Периодически встречаются лямбда-выражения, которые используются для подстановки в функции python, которые уже содержат функциональность для решения задачи. Нужно только посмотреть в документацию по функциям.

Например, возьмем этот код:

sorted_numbers = sorted(numbers, key=lambda n: abs(n))

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

Поскольку abs (которая возвращает абсолютное значение числа) является функцией и все функции могут быть переданы, мы могли бы написать приведенный выше код следующим образом:

sorted_numbers = sorted(numbers, key=abs)

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

pairs = [(4, 11), (8, 8), (5, 7), (11, 3)]
sorted_by_smallest = sorted(pairs, key=lambda items: min(items))

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

pairs = [(4, 11), (8, 8), (5, 7), (11, 3)]
sorted_by_smallest = sorted(pairs, key=min)

Вам не нужна лямбда-функция, если у вас уже есть другая функция, которая делает то, что вы хотите.

Замыкания: функции, возвращающие функции. Динамически сгенерированная функция

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

Вот очень простой пример:

def make_closure(a):
    def closure():
        print('Я знаю секрет: %d' % a)
    return closure

closure = make_closure(5)
closure()

Результат:

Я знаю секрет: 5

Разница между замыканием и обычной функцией Python состоит в том, что замыкание сохраняет доступ к пространству имен (функции), в котором было создано, даже если создавшая ее функция уже завершилась.

Так, в примере выше, возвращенное замыкание печатает строку «Я знаю секрет: 5», в какой бы момент ее ни вызвать. Хотя обычно создают замыкания со статическим внутренним состоянием (в данном случае только значение a), ничто не мешает включить в состав состояния изменяемый объект – словарь, множество, список – и затем модифицировать его.

Например, ниже приведена функция, которая возвращает функцию, запоминающую, с какими аргументами вызывалась объемлющая функция:

def make_watcher():
    have_seen = {}

    def has_been_seen(x):
        if x in have_seen:
            return True
        else:
            have_seen[x] = True
        return False

    return has_been_seen

watcher = make_watcher()

vals = [5, 6, 1, 5, 1, 6, 3, 5]

print([watcher(x) for x in vals])

Результат:

[False, False, False, True, True, True, False, True]

Однако следует иметь в виду одно техническое ограничение: изменять внутреннее состояние объектов (например, добавлять в словарь пары ключ-значение) можно, но связывать переменные в области видимости объемлющей функции – нельзя. Обойти это ограничение можно, например, путем модификации словаря или списка вместо присваивания значений переменным.

def make_counter():
    count = [0]

    def counter():
        # увеличить счетчик и вернуть новое значение
        count[0] += 1
        return count[0]
    return counter

counter = make_counter()
print(counter())

Результат:

1

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

Вот пример функции форматирования строк:

def format_and_pad(template, space):
    def formatter(x):
        return (template % x).rjust(space)
    return formatter

fmt = format_and_pad('%.4f', 15)
print(fmt(1.756))

Результат:

'         1.7560'

Каррирование: частичное фиксирование аргументов функции

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

Пусть, например, имеется тривиальная функция сложения двух чисел:

def add_numbers(x, y):
    return x + y

Мы можем породить на ее основе новую функцию одной переменной, add_five, которая прибавляет к своему аргументу 5:

add_five = lambda y: add_numbers(5, y)

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

Теперь запустим полный код:

def add_numbers(x, y):
    return x + y

add_five = lambda y: add_numbers(5, y)

print(add_five(10))

Результат:

15

Стандартный модуль functools упрощает эту процедуру за счет функции partial:

from functools import partial

def add_numbers(x, y):
    return x + y

add_five = partial(add_numbers, 5)

print(add_five(10))

Также вернет 15

При обсуждении библиотеки pandas мы будем пользоваться этой техникой для создания специализированных функций преобразования временных рядов:

# вычислить скользящее среднее временного ряда x за 60 дней
ma60 = lambda x: pandas.rolling_mean(x, 60)
# вычислить скользящие средние за 60 дней всех временных рядов в data
data.apply(ma60)

5 8 голоса
Рейтинг статьи
Шамаев Иван
Разработчик аналитических решений QlikView/Qlik Sense/Power BI; Python Data Scientist; Разработчик интеграционных решений на PHP, Python, JavaScript.
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x