Содержание

def (функция/метод) в Python

Последовательность инструкций, возвращающая некое значение.

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

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

Функции без инструкции return (равно как и с нею, но без указания аргумента) всё равно возвращают результат — None.

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

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

Первой инструкцией в теле может быть литерал строки, который будет являться документацией для данной функции (строка документации — «docstring»). Некоторые утилиты и среды разработки используют такие строки для формирования интерактивной справки.

Документировать код считается хорошим тоном.

def do_work(work, reverse=False):
"""Выполняет работу.

В случае удачного выполнения, возвращает True,
иначе - False.

:param list work: Список для работы.
:param bool reverse: Флаг. Следует ли сделать
работу в обратном порядке
:rtype: bool
"""


В примере выше объявляется функция do_work, с формальными параметрами work и reverse. Функция задокументирована (испольузется формат описания сигнатуры reStructuredText
). Кроме строки документации тело функции не содержит инструкций, тем не менее функция возвращает None.

Определение функции описывает пользовательский «объект функции» и является исполняемой инструкцией. В ходе исполнения происходит связывание имени функции в текущем локальном пространстве имён (локальной символьной таблице) с «объектом функции» — обёрткой вокруг исполняемого кода функции. Объект функции содержит ссылку на текущее глобальное пространство имён, которое будет использовано при вызове функции. Объект функции может быть в последующем связан и с другим именем (это можно использовать для переименования функций и создания псевдонимов).

Само определение функции не вызывает исполнения кода из тела функции. Код исполняется только при вызове функции.

    def print_yes():
print('yes')

print_yes() # yes

print_yes_alias = print_yes

print_yes_alias() # yes


Более того, ничто не мешает использовать «объект функции» как любой другой объект (например: передавать в функцию, использовать в качестве значения в словаре и т.п.).
    def print_yes():
print('yes')

def print_no():
print('no')

my_functions = [print_yes, print_no]

for function in my_functions:

# Переменная function будет содержать объект
# функции. Его-то мы и вызываем в следующей строке.
function()


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

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

global) нельзя.

    MY_GLOBAL = 1

def set_global_1():
MY_GLOBAL = 2

def set_global_2():
global MY_GLOBAL
MY_GLOBAL = 2

print(MY_GLOBAL) # 1

set_global_1()
print(MY_GLOBAL) # 1

set_global_2()
print(MY_GLOBAL) # 2


Аргументы, с которыми была вызвана функция, также оказываются в её локальной символьной таблице.

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

передачей по ссылке на объект».

    def mutate_list(a_list):
a_list.append(0)
return True

my_list = [1]
print(my_list) # [1]

mutate_list(my_list) # True
print(my_list) # [1, 0]


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

Вложенные определения

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

    def outer_func():

# Определение функции внутри другой
# функции.
def inner_func():
return 'some'

return inner_func()

print(outer_func()) # some

def get_my_class():

# Определение класса внутри определения
# функции.
class MyClass:

my_attr = 1

return MyClass

my_class = get_my_class()
print(my_class.my_attr) # 1


Синонимы поиска: def (функция/метод), function, define, return, функции, procedure, вуа

#12 — Функции (def, lambda, return)

В уроке мы затронем тему функций в языке Python.

Мы разберемся с ключевыми понятиями, такими как: def, lambda и return, а также мы научимся создавать функции и вызывать их. На основе функции нами будут построены различные мини программы.

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

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

Точно такая же ситуация обстоит с переменным. В классах переменные называются полями, а вне классов — переменными.

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

def some_test(): # Функция без параметров
	x = 23
	return x # Функция возвращает что-либо

def some_test_2(a, b, c): # Функция с 3 параметрами
	return a * b * c # Функция возвращает результат умножение чисел

def print_something(word, prefix): # Функция с 2 параметрами
	print (prefix, "-", word)
	pass # Функция ничего не возвращает

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

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

res = some_test_2 (2, 67, 12)

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

Анонимные функции

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

Для этого потребуется функция lambda

Пример создания «lambda» функции:

multiple = lambda a, b: a * b
multiple(7, 12)

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

Функции в программировании. Урок 10 курса «Python. Введение в программирование»

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

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

Существует множество встроенных в язык программирования функций. С некоторыми такими в Python мы уже сталкивались. Это print(), input(), int(), float(), str(), type(). Код их тела нам не виден, он где-то «спрятан внутри языка». Нам же предоставляется только интерфейс – имя функции.

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

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

i = 0
while i < 3:
    a = int(input())
    b = int(input())
    print(a+b)
    i += 1

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

print("Сколько бананов и ананасов для обезьян?")
a = int(input())
b = int(input())
print("Всего", a+b, "шт.")

print("Сколько жуков и червей для ежей?")
a = int(input())
b = int(input())
print("Всего", a+b, "шт.")

print("Сколько рыб и моллюсков для выдр?")
a = int(input())
b = int(input())
print("Всего", a+b, "шт.")

Пример исполнения программы:

Сколько бананов и ананасов для обезьян?
15
5
Всего 20 шт.
Сколько жуков и червей для ежей?
50
12
Всего 62 шт.
Сколько рыб и моллюсков для выдр?
16
8
Всего 24 шт.

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

Определение функции. Оператор def

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

def countFood():
    a = int(input())
    b = int(input())
    print("Всего", a+b, "шт.")

Это пример определения функции. Как и другие сложные инструкции вроде условного оператора и циклов функция состоит из заголовка и тела. Заголовок оканчивается двоеточием и переходом на новую строку. Тело имеет отступ.

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

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

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

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

Рассмотрим полную версию программы с функцией:

def countFood():
    a = int(input())
    b = int(input())
    print("Всего", a+b, "шт.")

print("Сколько бананов и ананасов для обезьян?")
countFood()

print("Сколько жуков и червей для ежей?")
countFood()

print("Сколько рыб и моллюсков для выдр?")
countFood()

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

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

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

print("Сколько бананов и ананасов для обезьян?")
countFood()

print("Сколько жуков и червей для ежей?")
countFood()

print("Сколько рыб и моллюсков для выдр?")
countFood()

def countFood():
    a = int(input())
    b = int(input())
    print("Всего", a+b, "шт.")

Результат:

Сколько бананов и ананасов для обезьян?
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    countFood()
NameError: name 'countFood' is not defined

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

Функции придают программе структуру

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

Пусть надо написать программу, вычисляющую площади разных фигур. Пользователь указывает, площадь какой фигуры он хочет вычислить. После этого вводит исходные данные. Например, длину и ширину в случае прямоугольника. Чтобы разделить поток выполнения на несколько ветвей, следует использовать оператор if-elif-else:

figure = input("1-прямоугольник, 
2-треугольник, 3-круг: ")

if figure == '1':
  a = float(input("Ширина: "))
  b = float(input("Высота: "))
  print("Площадь: %. 2f" % (a*b))
elif figure == '2':
  a = float(input("Основание: "))
  h = float(input("Высота: "))
  print("Площадь: %.2f" % (0.5 * a * h))
elif figure == '3':
  r = float(input("Радиус: "))
  print("Площадь: %.2f" % (3.14 * r**2))
else:
  print("Ошибка ввода")

Здесь нет никаких функций, и все прекрасно. Но напишем вариант с функциями:

def rectangle():
    a = float(input("Ширина: "))
    b = float(input("Высота: "))
    print("Площадь: %.2f" % (a*b))

def triangle():
    a = float(input("Основание: "))
    h = float(input("Высота: "))
    print("Площадь: %.2f" % (0.5 * a * h))

def circle():
    r = float(input("Радиус: "))
    print("Площадь: %.2f" % (3.14 * r**2))

figure = input("1-прямоугольник, 
2-треугольник, 3-круг: ")
if figure == '1':
  rectangle()
elif figure == '2':
  triangle()
elif figure == '3':
  circle()
else:
  print("Ошибка ввода")

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

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

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

Практическая работа

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

Основная ветка программы, не считая заголовков функций, состоит из одной строки кода. Это вызов функции test(). В ней запрашивается на ввод целое число. Если оно положительное, то вызывается функция positive(), тело которой содержит команду вывода на экран слова «Положительное». Если число отрицательное, то вызывается функция negative(), ее тело содержит выражение вывода на экран слова «Отрицательное».

Понятно, что вызов test() должен следовать после определения функций. Однако имеет ли значение порядок определения самих функций? То есть должны ли определения positive() и negative() предшествовать test() или могут следовать после него? Проверьте вашу гипотезу, поменяв объявления функций местами. Попробуйте объяснить результат.

Примеры решения и дополнительные уроки в android-приложении и pdf-версии курса

синтаксис, логика и применение ~ PythonRu

Введение

Определение

Вот пример простой функции:

def compute_surface(radius):
    from math import pi
    return pi * radius * radius

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

Последняя строка в блоке инструкций может начинаться с return, если нужно вернуть какое-то значение. Если инструкции return нет, тогда по умолчанию функция будет возвращать объект None. Как в этом примере:

i = 0
def increment():
    global i
    i += 1

Функция инкрементирует глобальную переменную i и возвращает None (по умолчанию).

Вызовы

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

surface = compute_surface(1.)

Для вызова функции, которая ничего не возвращает:

increment()

Еще

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

def sum(a, b): return a + b

Функции могут быть вложенными:

def func1(a, b):

    def inner_func(x):
        return x*x*x

    return inner_func(a) + inner_func(b)

Функции — это объекты, поэтому их можно присваивать переменным.

Инструкция return

Возврат простого значения

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

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

Пока что функция возвращала только одно значение или не возвращала ничего (объект None). А как насчет нескольких значений? Этого можно добиться с помощью массива. Технически, это все еще один объект. Например:

def stats(data):
    """данные должны быть списком"""
    _sum = sum(data) 
    mean = sum / float(len(data)) 
    variance = sum([(x-mean(data))**2/len(data) for x in data])
    return mean,variance   

m, v = stats([1, 2, 1])

Аргументы и параметры

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

Параметр — это имя в списке параметров в первой строке определения функции. Он получает свое значение при вызове. Аргумент — это реальное значение или ссылка на него, переданное функции при вызове. В этой функции:

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

x и y — это параметры, а в этой:

sum(1, 2)

1 и 2 — аргументы.

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

def compute_surface(radius, pi=3.14159):
    return pi * radius * radius

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

Выходит, что в следующем примере допущена ошибка:

def compute_surface(radius=1, pi):
    return pi * radius * radius

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

S = compute_surface(10, pi=3.14)

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

S = compute_surface(radius=10, pi=3.14)

А этот вызов некорректен:

S = compute_surface(pi=3.14, 10)

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

def compute_surface2(radius=1, pi=3.14159):
    return pi * radius * radius
S = compute_surface2(radius=1, pi=3.14)
S = compute_surface2(pi=3.14, radius=10.)
S = compute_surface2(radius=10.)

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

S = compute_surface2(10. , 3.14)
S = compute_surface2(10.)

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

def f(a=1,b=2, c=3):
    return a + b + c

Второй аргумент можно пропустить:

f(1,,3)

Чтобы обойти эту проблему, можно использовать словарь:

params = {'a':10, 'b':20}
S = f(**params)

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

def inplace(x, mutable=[]):
   mutable.append(x)
   return mutable
res = inplace(1)
res = inplace(2)
print(inplace(3))
[1, 2, 3]
def inplace(x, lst=None):
   if lst is None: lst=[]
   lst. append()
   return lst

Еще один пример изменяемого объекта, значение которого поменялось при вызове:

def change_list(seq):
    seq[0] = 100
original = [0, 1, 2]
change_list(lst1)
original
[100, 1, 2]

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

original = [0, 1, 2]
change_list(original[:])
original
[100, 1, 2]

Указание произвольного количества аргументов

Позиционные аргументы

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

def func(pos_params, *args):
    block statememt

При вызове функции нужно вводить команду следующим образом:

func(pos_params, arg1, arg2, . ..)

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

Вот так:

def add_mean(x, *data):
    return x + sum(data)/float(len(data))

add_mean(10,0,1,2,-1,0,-1,1,2)
10.5

Если лишние аргументы не указаны, значением по умолчанию будет пустой кортеж.

Произвольное количество аргументов-ключевых слов

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

def func(pos_params, *args, **kwargs):
    block statememt

При вызове функции нужно писать так:

func(pos_params, kw1=arg1, kw2=arg2, ...)

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

Есть функция:

def print_mean_sequences(**kwargs):
    def mean(data):
        return sum(data)/float(len(data))
    for k, v in kwargs.items():
        print k, mean(v)

print_mean_sequences(x=[1,2,3], y=[3,3,0])
y 2.0
x 2.0

Важно, что пользователь также может использовать словарь, но перед ним нужно ставить две звездочки (**):

print_mean_sequences(**{'x':[1,2,3], 'y':[3,3,0]})
y 2.0
x 2.0

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

Документирование функции

Определим функцию:

def sum(s,y): return x + y

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

def sum(x, y):
    """Первая срока - заголовок

    Затем следует необязательная пустая строка и текст 
    документации.
    """
    return x+y

Команда docstring должна быть первой инструкцией после объявления функции. Ее потом можно будет извлекать или дополнять:

print(sum.__doc__)
sum.__doc__ += "some additional text"

Методы, функции и атрибуты, связанные с объектами функции

Если поискать доступные для функции атрибуты, то в списке окажутся следующие методы (в Python все является объектом — даже функция):

sum.func_closure   sum.func_defaults  sum.func_doc       sum.func_name
sum.func_code      sum.func_dict      sum.func_globals

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

>>> sum.__name__
"sum"
>>> sum.__module
"__main__"

Есть и другие. Вот те, которые не обсуждались:

sum.__call__          sum.__delattr__       sum.__getattribute__     sum.__setattr__
sum.__class__         sum.__dict__          sum.__globals__       sum.__new__           sum.__sizeof__
sum.__closure__       sum.__hash__          sum.__reduce__        sum.__str__
sum.__code__          sum.__format__        sum.__init__          sum.__reduce_ex__     sum.__subclasshook__
sum.__defaults__      sum.__get__           sum.__repr__

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

Рекурсия — это не особенность Python. Это общепринятая и часто используемая техника в Computer Science, когда функция вызывает сама себя. Самый известный пример — вычисление факториала n! = n * n — 1 * n -2 * … 2 *1. Зная, что 0! = 1, факториал можно записать следующим образом:

def factorial(n):
    if n != 0:
        return n * factorial(n-1)
    else:
        return 1

Другой распространенный пример — определение последовательности Фибоначчи:

f(0) = 1
f(1) = 1
f(n) = f(n-1) + f(n-2)

Рекурсивную функцию можно записать так:

def fibbonacci(n):
    if n >= 2:
        else:
    return 1

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

def factorial(n):
    assert n > 0
    if n != 0:
        return n * factorial(n-1)
    else:
        return 1

Важно!
Рекурсия позволяет писать простые и элегантные функции, но это не гарантирует эффективность и высокую скорость исполнения.

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

Глобальная переменная

Вот уже знакомый пример с глобальной переменной:

i = 0
def increment():
    global i
    i += 1

Здесь функция увеличивает на 1 значение глобальной переменной i. Это способ изменять глобальную переменную, определенную вне функции. Без него функция не будет знать, что такое переменная i. Ключевое слово global можно вводить в любом месте, но переменную разрешается использовать только после ее объявления.

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

Присвоение функции переменной

С существующей функцией func синтаксис максимально простой:

variable = func

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

Менять название переменной также разрешается:

def func(x): return x
a1 = func
a1(10)
10
a2 = a1
a2()
10

В этом примере a1, a2 и func имеют один и тот же id. Они ссылаются на один объект.

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

def sq(x): return x*x

Позже ее можно переименовать, используя более осмысленное имя. Первый вариант — просто сменить имя. Проблема в том, что если в другом месте кода используется sq, то этот участок не будет работать. Лучше просто добавить следующее выражение:

square = sq

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

dir = 3

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

del dir
dir()

Анонимная функция: лямбда

Лямбда-функция — это короткая однострочная функция, которой даже не нужно имя давать. Такие выражения содержат лишь одну инструкцию, поэтому, например, if, for и while использовать нельзя. Их также можно присваивать переменным:

product = lambda x,y: x*y

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

С помощью type() можно проверить тип:

>>> type(product)
function

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

power = lambda x=1, y=2: x**y
square = power
square(5.)
25
power = lambda x,y,pow=2: x**pow + y
[power(x,2, 3) for x in [0,1,2]]
[2, 3, 10]

Изменяемые аргументы по умолчанию

>>> def foo(x=[]):
...     x.append(1)
...     print x
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

Вместо этого нужно использовать значение «не указано» и заменить на изменяемый объект по умолчанию:

>>> def foo(x=None):
...     if x is None:
. ..         x = []
...     x.append(1)
...     print x
>>> foo()
[1]
>>> foo()
[1]

Интерактивный учебник языка Python

1. Функции

Напомним, что в математике факториал числа n определяется как n! = 1 ⋅ 2 ⋅ … ⋅ n. Например, 5! = 1 ⋅ 2 ⋅ 3 ⋅ 4 ⋅ 5 = 120. Ясно, что факториал можно легко посчитать, воспользовавшись циклом for. Представим, что нам нужно в нашей программе вычислять факториал разных чисел несколько раз (или в разных местах кода). Конечно, можно написать вычисление факториала один раз, а затем используя Copy-Paste вставить его везде, где это будет нужно.

	
# вычислим 3!
res = 1
for i in range(1, 4):
    res *= i
print(res)

# вычислим 5!
res = 1
for i in range(1, 6):
    res *= i
print(res)

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

Функции — это такие участки кода, которые изолированы от остальный программы и выполняются только тогда, когда вызываются. Вы уже встречались с функциями sqrt(), len() и print(). Они все обладают общим свойством: они могут принимать параметры (ноль, один или несколько), и они могут возвращать значение (хотя могут и не возвращать). Например, функция sqrt() принимает один параметр и возвращает значение (корень числа). Функция print() принимает переменное число параметров и ничего не возвращает.

Покажем, как написать функцию factorial(), которая принимает один параметр — число, и возвращает значение — факториал этого числа.

	
def factorial(n):
    res = 1
    for i in range(1, n + 1):
        res *= i
    return res

print(factorial(3))
print(factorial(5))

Дадим несколько объяснений. Во-первых, код функции должен размещаться в начале программы, вернее, до того места, где мы захотим воспользоваться функцией factorial(). Первая строчка этого примера является описанием нашей функции. factorial — идентификатор, то есть имя нашей функции. После идентификатора в круглых скобках идет список параметров, которые получает наша функция. Список состоит из перечисленных через запятую идентификаторов параметров. В нашем случае список состоит из одной величины n. В конце строки ставится двоеточие.

Далее идет тело функции, оформленное в виде блока, то есть с отступом. Внутри функции вычисляется значение факториала числа n и оно сохраняется в переменной res. Функция завершается инструкцией return res, которая завершает работу функции и возвращает значение переменной res.

Инструкция return может встречаться в произвольном месте функции, ее исполнение завершает работу функции и возвращает указанное значение в место вызова. Если функция не возвращает значения, то инструкция return используется без возвращаемого значения. В функциях, которым не нужно возвращать значения, инструкция return может отсутствовать.

Приведём ещё один пример. Напишем функцию max(), которая принимает два числа и возвращает максимальное из них (на самом деле, такая функция уже встроена в Питон).

10
20
def max(a, b):
    if a > b:
        return a
    else:
        return b

print(max(3, 5))
print(max(5, 3))
print(max(int(input()), int(input())))

Теперь можно написать функцию max3(), которая принимает три числа и возвращает максимальное их них.

	
def max(a, b):
    if a > b:
        return a
    else:
        return b

def max3(a, b, c):
    return max(max(a, b), c)

print(max3(3, 5, 4))
Встроенная функция max() в Питоне может принимать переменное число аргументов и возвращать максимум из них. Приведём пример того, как такая функция может быть написана.
	
def max(*a):
    res = a[0]
    for val in a[1:]:
        if val > res:
            res = val
    return res

print(max(3, 5, 4))
Все переданные в эту функцию параметры соберутся в один кортеж с именем a, на что указывает звёздочка в строке объявления функции.

2. Локальные и глобальные переменные

Внутри функции можно использовать переменные, объявленные вне этой функции

	
def f():
    print(a)

a = 1
f()

Здесь переменной a присваивается значение 1, и функция f() печатает это значение, несмотря на то, что до объявления функции f эта переменная не инициализируется. В момент вызова функции f() переменной a уже присвоено значение, поэтому функция f() может вывести его на экран.

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

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

	
def f():
    a = 1

f()
print(a)

Получим ошибку NameError: name 'a' is not defined. Такие переменные, объявленные внутри функции, называются локальными. Эти переменные становятся недоступными после выхода из функции.

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

	
def f():
    a = 1
    print(a)

a = 0
f()
print(a)

Будут выведены числа 1 и 0. Несмотря на то, что значение переменной a изменилось внутри функции, вне функции оно осталось прежним! Это сделано в целях “защиты” глобальных переменных от случайного изменения из функции. Например, если функция будет вызвана из цикла по переменной i, а в этой функции будет использована переменная i также для организации цикла, то эти переменные должны быть различными. Если вы не поняли последнее предложение, то посмотрите на следующий код и подумайте, как бы он работал, если бы внутри функции изменялась переменная i.

	
def factorial(n):
    res = 1
    for i in range(1, n + 1):
        res *= i
    return res

for i in range(1, 6):
    print(i, '! = ', factorial(i), sep='')
Если бы глобальная переменная i изменялась внутри функции, то мы бы получили вот что:
5! = 1
5! = 2
5! = 6
5! = 24
5! = 120
Итак, если внутри функции модифицируется значение некоторой переменной, то переменная с таким именем становится локальной переменной, и ее модификация не приведет к изменению глобальной переменной с таким же именем.

Более формально: интерпретатор Питон считает переменную локальной для данной функции, если в её коде есть хотя бы одна инструкция, модифицирующая значение переменной, то эта переменная считается локальной и не может быть использована до инициализации. Инструкция, модифицирующая значение переменной — это операторы =, +=, а также использование переменной в качестве параметра цикла for. При этом даже если инструкция, модицифицирующая переменную никогда не будет выполнена, интерпретатор это проверить не может, и переменная все равно считается локальной. Пример:

	
def f():
    print(a)
    if False:
        a = 0

a = 1
f()

Возникает ошибка: UnboundLocalError: local variable 'a' referenced before assignment. А именно, в функции f() идентификатор a становится локальной переменной, т.к. в функции есть команда, модифицирующая переменную a, пусть даже никогда и не выполняющийся (но интерпретатор не может это отследить). Поэтому вывод переменной a приводит к обращению к неинициализированной локальной переменной.

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

	
def f():
    global a
    a = 1
    print(a)

a = 0
f()
print(a)

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

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

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

5
def factorial(n):
    global f
    res = 1
    for i in range(2, n + 1):
        res *= i
    f = res

n = int(input())
factorial(n)
# дальше всякие действия с переменной f

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

Гораздо лучше переписать этот пример так:

5
# начало куска кода, который можно копировать из программы в программу
def factorial(n):
    res = 1
    for i in range(2, n + 1):
        res *= i
    return res
# конец куска кода

n = int(input())
f = factorial(n)
# дальше всякие действия с переменной f

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

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

3.

Рекурсия
def short_story():
    print("У попа была собака, он ее любил.")
    print("Она съела кусок мяса, он ее убил,")
    print("В землю закопал и надпись написал:")
    short_story()

Как мы видели выше, функция может вызывать другую функцию. Но функция также может вызывать и саму себя! Рассмотрим это на примере функции вычисления факториала. Хорошо известно, что 0!=1, 1!=1. А как вычислить величину n! для большого n? Если бы мы могли вычислить величину (n-1)!, то тогда мы легко вычислим n!, поскольку n!=n⋅(n-1)!. Но как вычислить (n-1)!? Если бы мы вычислили (n-2)!, то мы сможем вычисли и (n-1)!=(n-1)⋅(n-2)!. А как вычислить (n-2)!? Если бы… В конце концов, мы дойдем до величины 0!, которая равна 1. Таким образом, для вычисления факториала мы можем использовать значение факториала для меньшего числа. Это можно сделать и в программе на Питоне:

	
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

print(factorial(5))

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

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

  1. Неправильное оформление выхода из рекурсии. Например, если мы в программе вычисления факториала забудем поставить проверку if n == 0, то factorial(0) вызовет factorial(-1), тот вызовет factorial(-2) и т. д.
  2. Рекурсивный вызов с неправильными параметрами. Например, если функция factorial(n) будет вызывать factorial(n), то также получится бесконечная цепочка.

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

Ссылки на задачи доступны в меню слева. Эталонные решения теперь доступны на странице самой задачи.

Python def function: как вы указываете конец функции?



Я просто изучаю python и запутываюсь, когда «def» функции заканчивается?

Я вижу примеры кода, такие как:

def myfunc(a=4,b=6):
    sum = a + b
    return sum

myfunc()

Я знаю, что это не заканчивается из-за возврата (потому что я видел операторы if… if FOO than return BAR, else return FOOBAR). Откуда Python знает, что это не рекурсивная функция, которая сама себя вызывает? Когда функция запускается, она просто продолжает проходить через программу, пока не найдет возврат? Это привело бы к некоторым интересным ошибкам.

Спасибо

python syntax
Поделиться Источник Dan     15 октября 2009 в 16:32

8 ответов


  • Список портов с помощью функции Def (Python)

    У меня проблемы с пониманием этого задания. В настоящее время я изучаю использование функции define (Извините, я очень новичок в Python). И хотя я знаю, как использовать функцию define, я не совсем понимаю, как решить эту проблему. Я понимаю, как работает функция def, но поправьте меня, если я…

  • Python grammar конце «Возвращения за пределами функции»

    Я заметил, что Python grammar позволяет оператору return появляться вне функции, но я действительно не понимаю, почему? Я считаю, что можно указать grammar так, чтобы это не было разрешено. Это кусок Python grammar, который позволяет это: single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE…



66

В Python whitespace значимо. Функция заканчивается, когда отступ становится меньше (меньше).

def f():
    pass # first line
    pass # second line
pass # <-- less indentation, not part of function f.

Обратите внимание, что однострочные функции могут быть записаны без отступа, на одной строке:

def f(): pass

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

def f(): pass; pass

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

  • Используя оператор return . Это работает так же, как и в любом другом императивном языке программирования, который вы можете знать.

  • Используя оператор yield . Это означает, что функция является генератором. Объяснение его семантики выходит за рамки этого ответа. Посмотрите, может ли кто-нибудь объяснить мне заявление о доходности python?

  • Просто выполнив последнее утверждение. Если больше нет операторов и последний оператор не является оператором return , то функция существует так, как если бы последний оператор был return None . То есть без явного оператора return функция возвращает None . Эта функция возвращает None :

    def f():
        pass
    

    И этот тоже:

    def f():
        42
    

Поделиться Stephan202     15 октября 2009 в 16:34



35

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

Поделиться fforw     15 октября 2009 в 16:34



9

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

def foo():
    if bar:
        print "bar"

print "baz" # ends the if and foo at the same time

Уровень отступа равен less-than-or-equal как для def, так и для if, следовательно, он заканчивает их оба.

Строки без утверждения, независимо от отступа, не имеют значения

def foo():
    print "The line below has no indentation"

    print "Still part of foo"

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

def foo():
    print "Still correct"
   print "Error because there is no block at this indentation"

Как правило, если вы привыкли к языку фигурных скобок, просто сделайте отступ в коде, как они, и все будет в порядке.

BTW, способ отступа «standard» — только с пробелами, но, конечно, возможна только табуляция, но, пожалуйста, не смешивайте их оба.

Поделиться RichN     15 октября 2009 в 16:52




4

Интересно, что если вы просто набираете текст в интерактивном интерпретаторе python, вы должны следовать за функцией с пустой строкой. Это не работает:

def foo(x):
  return x+1
print "last"

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

Поделиться GaryO     21 марта 2013 в 00:54



2

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

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

в учебнике есть еще много информации .

Поделиться SilentGhost     15 октября 2009 в 16:34



1

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

Поделиться Mohan Gulati     15 октября 2009 в 17:56



1

Он использует отступы

 def func():
     funcbody
     if cond: 
         ifbody
     outofif

 outof_func 

Поделиться OscarRyz     15 октября 2009 в 16:35



0

На мой взгляд, лучше явно отметить конец функции комментарием

def func():
     # funcbody
     ## end of subroutine func ##

Дело в том, что какая-то подпрограмма очень длинная и не удобно прокручивать редактор вверх, чтобы проверить, для какой функции она закончилась. Кроме того, если вы используете Sublime, вы можете щелкнуть правой кнопкой мыши -> goto Definition, и он автоматически перейдет к объявлению подпрограммы.

Поделиться kstn     13 декабря 2019 в 17:50


Похожие вопросы:


Python функция как аргумент функции?

Может ли функция Python быть аргументом другой функции? Скажи: def myfunc(anotherfunc, extraArgs): # run anotherfunc and also pass the values from extraArgs to it pass Так что это в основном два…


Локальные функции в Python

В следующем коде Python я получаю UnboundLocalError . Насколько я понимаю, локальные функции разделяют локальные переменные содержащей функции, но здесь это вряд ли имеет место. Я признаю, что a…


Как я могу указать тип функции в подсказках типа?

Я хочу использовать подсказки типа в моем текущем проекте Python 3.5. Моя функция должна получать функцию в качестве параметра. Как я могу указать функцию типа в моих подсказках типа? import typing…


Список портов с помощью функции Def (Python)

У меня проблемы с пониманием этого задания. В настоящее время я изучаю использование функции define (Извините, я очень новичок в Python). И хотя я знаю, как использовать функцию define, я не совсем…


Python grammar конце «Возвращения за пределами функции»

Я заметил, что Python grammar позволяет оператору return появляться вне функции, но я действительно не понимаю, почему? Я считаю, что можно указать grammar так, чтобы это не было разрешено. Это…


Функция добавления элемента в конец связанного списка

Я новичок в программировании вообще и только начал использовать python. Недавно я узнал о связанных списках, и у меня возникли проблемы с использованием функции добавления элемента в конец моего…


Как явно указать использование значения по умолчанию именованного аргумента в функции python?

Можно ли явно указать функции в python использовать значение параметра по умолчанию ? Например, если функция определена как: def foo(a=1, b=2, c=3): pass Я хотел бы вызвать эту функцию следующим. ..


Как указать тип return функции при возврате нескольких значений?

В Python мы можем указать тип функции return следующим образом def foo() -> int: some_int = get_some_int() return some_int Как указать типы return при возврате нескольких значений? Например, как…


Python функция из другой функции def

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


Функция python def и input

Я пытаюсь настроить функцию с помощью input , возможно ли это ? function = input(‘Please enter a function example’) def f(x): return function print(f(2)) Поэтому, если функция input равна x**2, она…

Как использовать в Python лямбда-функции

В Python и других языках, таких как Java, C# и даже C++, в их синтаксис добавлены лямбда-функции, в то время как языки, такие как LISP или семейство языков ML, Haskell, OCaml и F#, используют лямбда-выражения.

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

К концу этой статьи вы узнаете:

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

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

Эти сомнительные примеры будут противопоставляться лучшим подходам или альтернативам по мере прохождения статьи.

Все примеры, включенные в это руководство, были протестированы в Python 3.7.

Лямбда-исчисление

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

История

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

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

Функциональные языки берут свое начало в математической логике и лямбда-исчислении, в то время как императивные языки программирования охватывают основанную на состоянии модель вычислений, изобретенную Аланом Тьюрингом. Две модели вычислений, лямбда-исчисление и машины Тьюринга, могут быть переведены друг в друга. Эта эквивалентность известна как гипотеза Чёрча-Тьюринга.

Функциональные языки напрямую наследуют философию лямбда-исчисления, применяя декларативный подход программирования, которое придает особое значение абстракции, преобразование данных, композицию и чистоту (без состояния и без побочных эффектов). Примерами функциональных языков являются Haskell, Lisp или Erlang.

Напротив, машина Тьюринга привела к императивному программированию, используемому в таких языках, как Fortran, C или Python.

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

Разделение в обоих подходах относительное, поскольку некоторые функциональные языки включают императивные функции, такие как OCaml, в то время как функциональные функции проникают в императивное семейство языков, в частности, с введением лямбда-функций в Java или Python.

Python по своей сути не является функциональным языком, но на раннем этапе он принял некоторые функциональные концепции. В январе 1994 года к языку были добавлены map(), filter(), reduce() и лямбда-оператор.

Первый пример

Вот несколько примеров, чтобы продемонстрировать функциональный стиль.

Функция тождества (identity function), функция, которая возвращает свой аргумент, выражается стандартным определением функции Python с использованием ключевого слова def следующим образом:

>>> def identity(x):
...   return x

identity() принимает аргумент x и возвращает его при вызове.

Если вы воспользуетесь лямбда-конструкцией, ваш код будет следующим:

>>> lambda x: x

В приведенном выше примере выражение состоит из:

  • Ключевое слово: lambda
  • Связанная переменная: x
  • Тело: х

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

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

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

>>> lambda x: x + 1

Применим указанную выше функцию к аргументу, заключив функцию и ее аргумент в круглые скобки:

>>> (lambda x: x + 1)(2)
3

Сокращение — это стратегия лямбда-исчисления для вычисления значения выражения. Оно состоит из замены аргумента x на 2:

(lambda x: x + 1)(2) = lambda 2: 2 + 1
                     = 2 + 1
                     = 3

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

>>> add_one = lambda x: x + 1
>>> add_one(2)
3

Вышеупомянутая лямбда-функция эквивалентна написанию этого:

def add_one(x):
    return x + 1

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

>>> full_name = lambda first, last: f'Full name: {first.title()} {last.title()}'
>>> full_name('guido', 'van rossum')
'Full name: Guido Van Rossum'

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

Анонимные функции

Следующие термины могут использоваться взаимозаменяемо в зависимости от языка программирования:

  • Анонимные функции
  • Лямбда-функции
  • Лямбда-выражения
  • Лямбда-абстракции
  • Лямбда-форма
  • Функциональные литералы

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

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

>>> lambda x, y: x + y

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

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

>>> _(1, 2)
3

В приведенном выше примере используется только функция интерактивного транслятора, представленная через символ подчеркивания (_).

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

Примечание. В интерактивном интерпретаторе подчеркивание (_) привязано к последнему вычисленному выражению.

Для получения более подробной информации об использовании этого специального символа в Python, посмотрите Значение подчеркивания в Python (The Meaning of Underscores in Python).

Другой шаблон, используемый в других языках, таких как JavaScript, — это немедленное выполнение лямбда-функции Python. Это называется выражением немедленного вызова функции (IIFE —  Immediately Invoked Function Expression, произносится «iffy»). Вот пример:

>>> (lambda x, y: x + y)(2, 3)
5

Вышеприведенная лямбда-функция определяется, а затем сразу вызывается с двумя аргументами (2 и 3). Возвращает значение 5, которое является суммой аргументов.

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

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

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

>>> high_ord_func = lambda x, func: x + func(x)
>>> high_ord_func(2, lambda x: x * x)
6
>>> high_ord_func(2, lambda x: x + 3)
7

Python содержит функции высшего порядка в виде встроенных функций или в стандартной библиотеке. Примеры функций высшего порядка map(), filter(), functools.reduce(), а также такие ключевые функции, как sort(), sorted(), min() и max(). Мы продемонстрируем использование лямбда-функции вместе с функциями высшего порядка в разделе «Соответствующее использование лямбда-выражений».

Лямбда и обычные функции

Эта цитата из часто задаваемых вопросов по Python Design and History FAQ, похоже, задает тон в отношении общего ожидания использования лямбда-функций в Python:

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

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

Функции

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

Модуль dis предоставляет функции для анализа байт-кода Python, сгенерированного компилятором Python:

>>> import dis
>>> add = lambda x, y: x + y
>>> type(add)
<class 'function'>
>>> dis.dis(add)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> add
<function <lambda> at 0x7f30c6ce9ea0>

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

Теперь посмотрим на обычный объект функции:

>>> import dis
>>> def add(x, y): return x + y
>>> type(add)
<class 'function'>
>>> dis.dis(add)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> add
<function add at 0x7f30c6ce9f28>

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

Traceback

В предыдущем разделе вы видели, что в контексте лямбда-функции Python не предоставлял имя функции, а только <lambda> . Это может быть ограничением, которое следует учитывать при возникновении исключения, и в результате трассировки отображается только:

>>> div_zero = lambda x: x / 0
>>> div_zero(2)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 1, in <lambda>
ZeroDivisionError: division by zero

Трассировка исключения, возникшего при выполнении лямбда-функции, идентифицирует только функцию, вызывающую исключение, как <lambda> .

Вот то же исключение, вызванное в нормальной функции:

>>> def div_zero(x): return x / 0
>>> div_zero(2)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 1, in div_zero
ZeroDivisionError: division by zero

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

Синтаксис

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

  • Она может содержать только выражения и не может включать операторы в свое тело.
  • Она пишется как одна строка исполнения.
  • Она не поддерживает аннотации типов.
  • Она может быть немедленно вызвана (IIFE).

Отсутствие утверждений

Лямбда-функция не может содержать утверждения. В лямбда-функции такие операторы, как return, pass, assert или raise, вызовут исключение SyntaxError. Вот пример добавления assert к телу лямбды:

>>> (lambda x: assert x == 2)(2)
  File "<input>", line 1
    (lambda x: assert x == 2)(2)
                    ^
SyntaxError: invalid syntax

Этот надуманный пример демонстрирующий что с помощью assert, утверждается что параметр x имеет значение 2. Но интерпретатор выдает SyntaxError при синтаксическом анализе кода, который включает в себя утверждение assert в теле лямбда-выражения.

Одиночное выражение

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

>>> (lambda x:
... (x % 2 and 'odd' or 'even'))(3)
'odd'

Приведенный выше пример возвращает строку «odd», если лямбда-аргумент нечетный, и «even», когда аргумент четный. Он распространяется на две строки, поскольку содержится в скобках, но остается одним выражением.

Аннотации типов

Если вы начали применять анотации типов, которые теперь доступны в Python, у вас есть еще одна веская причина предпочесть нормальные функции лямбда-функциям Python. В лямбда-функции нет эквивалента для следующего:

def full_name(first: str, last: str) -> str:
    return f'{first.title()} {last.title()}'

Любая ошибка типа в full_name() может быть обнаружена такими инструментами, как mypy или pyre, тогда как в эквивалентной лямбда-функцией сразу будет ошибка SyntaxError во время выполнения:

>>> lambda first: str, last: str: first.title() + " " + last.title() -> str
  File "<stdin>", line 1
    lambda first: str, last: str: first.title() + " " + last.title() -> str

SyntaxError: invalid syntax

IIFE

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

>>> (lambda x: x * x)(3)
9

Вне интерпретатора эта функция, вероятно, не будет используется на практике. Это прямое следствие того, что лямбда-функция вызывается сразу после того, как она определена. Но, это конструкция позволяет передать определение лямбды в функцию более высокого порядка, например map(), filter() или functools.reduce().

Аргументы

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

  • Позиционные аргументы
  • Именованные аргументы (иногда называемые ключевыми аргументами)
  • Переменный список аргументов (часто называемый varargs)
  • Переменный список аргументов ключевых слов
  • Аргументы только для ключевых слов

Следующие примеры иллюстрируют опции, доступные для передачи аргументов в лямбда-выражения:

>>> (lambda x, y, z: x + y + z)(1, 2, 3)
6
>>> (lambda x, y, z=3: x + y + z)(1, 2)
6
>>> (lambda x, y, z=3: x + y + z)(1, y=2)
6
>>> (lambda *args: sum(args))(1,2,3)
6
>>> (lambda **kwargs: sum(kwargs. values()))(one=1, two=2, three=3)
6
>>> (lambda x, *, y=0, z=0: x + y + z)(1, y=2, z=3)
6

Декораторы

В Python декоратор — это реализация шаблона, который позволяет добавить поведение к функции или классу. Обычно это выражается синтаксисом @decorator с префиксом функции. Вот пример:

def some_decorator(f):
    def wraps(*args):
        print(f"Calling function '{f.__name__}'")
        return f(args)
    return wraps

@some_decorator
def decorated_function(x):
    print(f"With argument '{x}'")

В приведенном выше примере some_decorator() — это функция, которая добавляет поведение к decorated_function(), так что при вызове decorated_function(2) получается следующий результат:

Calling function 'decorated_function'
With argument 'Python'

decorated_function() печатает только With argument ‘Python’, но декоратор добавляет дополнительное поведение, которое также печатает Calling function ‘decorated_function’.

Декоратор может быть применен к лямбде. Хотя невозможно декорировать лямбду с помощью синтаксиса @decorator, декоратор — это просто функция, поэтому он может вызывать функцию лямбда:

 1 # Defining a decorator
 2 def trace(f):
 3     def wrap(*args, **kwargs):
 4         print(f"[TRACE] func: {f.__name__}, args: {args}, kwargs: {kwargs}")
 5         return f(*args, **kwargs)
 6 
 7     return wrap
 8 
 9 # Applying decorator to a function
10 @trace
11 def add_two(x):
12     return x + 2
13 
14 # Calling the decorated function
15 add_two(3)
16 
17 # Applying decorator to a lambda
18 print((trace(lambda x: x ** 2))(3))

add_two(), декорирована @trace в строке 11, вызывается с аргументом 3 в строке 15. В отличие от этого, в строке 18 сразу же включается лямбда-функция и встраивается в вызов метода trace(), декоратора. Когда вы выполняете код выше, вы получаете следующее:

[TRACE] func: add_two, args: (3,), kwargs: {}
[TRACE] func: <lambda>, args: (3,), kwargs: {}
9

Посмотрите, как, как вы уже видели, имя лямбда-функции выглядит как <lambda>, тогда как add_two четко идентифицировано как обычная функция.

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

list(map(trace(lambda x: x*2), range(3)))

Первый аргумент map() — это лямбда, которая умножает свой аргумент на 2. Эта лямбда декорирована trace(). При выполнении приведенный выше пример выводит следующее:

[TRACE] Calling <lambda> with args (0,) and kwargs {}
[TRACE] Calling <lambda> with args (1,) and kwargs {}
[TRACE] Calling <lambda> with args (2,) and kwargs {}
[0, 2, 4]

Результат [0, 2, 4] представляет собой список, полученный умножением каждого элемента range(3). range(3) является простым списком [0, 1, 2].

Замыкание

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

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

Вот пример замыкания, построенное с помощью обычной функции Python:

 1 def outer_func(x):
 2     y = 4
 3     def inner_func(z):
 4         print(f"x = {x}, y = {y}, z = {z}")
 5         return x + y + z
 6     return inner_func
 7 
 8 for i in range(3):
 9     closure = outer_func(i)
10     print(f"closure({i+5}) = {closure(i+5)}")

outer_func() возвращает inner_func(), вложенную функцию, которая вычисляет сумму трех аргументов:

  • x передается в качестве аргумента outer_func().
  • y является локальной переменной для outer_func().
  • z аргумент, передаваемый в inner_func().

Чтобы продемонстрировать поведение outer_func() и inner_func(), outer_func() вызывается три раза в цикле for, который выводит следующее:

x = 0, y = 4, z = 5
closure(5) = 9
x = 1, y = 4, z = 6
closure(6) = 11
x = 2, y = 4, z = 7
closure(7) = 13

В строке 9 кода inner_func(), возвращаемый вызовом outer_func(), привязывается к имени замыкания. В строке 5 inner_func() захватывает x и y, потому что он имеет доступ к своей области видимости, так что при вызове замыкания он может работать с двумя свободными переменными x и y.

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

def outer_func(x):
    y = 4
    return lambda z: x + y + z

for i in range(3):
    closure = outer_func(i)
    print(f"closure({i+5}) = {closure(i+5)}")

Когда вы выполняете приведенный выше код, вы получаете следующий вывод:

closure(5) = 9
closure(6) = 11
closure(7) = 13

В строке 6 outer_func() возвращает лямбду и присваивает ее переменную замыкания. В строке 3 тело лямбда-функции ссылается на x и y. Переменная y доступна во время определения, тогда как x определяется во время выполнения, когда вызывается outer_func().

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

Время оценки

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

Сначала протестируем сценарий, используя обычную функцию:

 1 >>> def wrap(n):
 2 ...     def f():
 3 ...         print(n)
 4 ...     return f
 5 ...
 6 >>> numbers = 'one', 'two', 'three'
 7 >>> funcs = []
 8 >>> for n in numbers:
 9 . ..     funcs.append(wrap(n))
10 ...
11 >>> for f in funcs:
12 ...     f()
13 ...
14 one
15 two
16 three

В нормальной функции n вычисляется во время определения, в строке 9, когда функция добавляется в список: funcs.append (wrap (n)).

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

 1 >>> numbers = 'one', 'two', 'three'
 2 >>> funcs = []
 3 >>> for n in numbers:
 4 ...     funcs.append(lambda: print(n))
 5 ...
 6 >>> for f in funcs:
 7 ...     f()
 8 ...
 9 three
10 three
11 three

Неожиданный результат возникает из-за того, что свободная переменная n, как она реализована, связана во время выполнения лямбда-выражения. Лямбда-функция Python в строке 4 является замыканием, которое захватывает n, свободную переменную, ограниченную во время выполнения. Во время выполнения при вызове функции f из строки 7 значение n равно three.

Чтобы решить эту проблему, вы можете назначить свободную переменную во время определения следующим образом:

 1 >>> numbers = 'one', 'two', 'three'
 2 >>> funcs = []
 3 >>> for n in numbers:
 4 ...     funcs.append(lambda n=n: print(n))
 5 ...
 6 >>> for f in funcs:
 7 ...     f()
 8 ...
 9 one
10 two
11 three

Лямбда ведет себя как нормальная функция в отношении аргументов. Следовательно, лямбда-параметр может быть инициализирован значением по умолчанию: параметр n принимает значение n по умолчанию для внешнего n. Лямбда может бы быть записана как lambda x=n: print(x) и вернуть такой же результат.

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

Тестирование Лямбды

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

unittest

Модуль unittest обрабатывает лямбда-функции Python аналогично обычным функциям:

import unittest

addtwo = lambda x: x + 2

class LambdaTest(unittest.TestCase):
    def test_add_two(self):
        self.assertEqual(addtwo(2), 4)

    def test_add_two_point_two(self):
        self.assertEqual(addtwo(2.2), 4.2)

    def test_add_three(self):
        # Should fail
        self.assertEqual(addtwo(3), 6)

if __name__ == '__main__':
    unittest. main(verbosity=2)

LambdaTest определяет тестовый пример с тремя методами тестирования, каждый из которых использует сценарий тестирования для addtwo(), реализованной как лямбда-функция. Выполнение Python-файла lambda_unittest.py, содержащего LambdaTest, приводит к следующему:

$ python lambda_unittest.py
test_add_three (__main__.LambdaTest) ... FAIL
test_add_two (__main__.LambdaTest) ... ok
test_add_two_point_two (__main__.LambdaTest) ... ok

======================================================================
FAIL: test_add_three (__main__.LambdaTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "lambda_unittest.py", line 18, in test_add_three
    self. assertEqual(addtwo(3), 6)
AssertionError: 5 != 6

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (failures=1)

Как и ожидалось, у нас есть два успешных тестовых примера и один сбой для test_add_three: результат равен 5, но ожидаемый результат равен 6. Этот сбой вызван преднамеренной ошибкой в тестовом примере. Изменение ожидаемого результата с 6 на 5 удовлетворит все тесты для LambdaTest.

doctest

Модуль doctest извлекает интерактивный код Python из docstring для выполнения тестов. Хотя синтаксис лямбда-функций Python не поддерживает типичную docstring, можно присвоить строку элементу __doc__ именованной переменной лямбды:

addtwo = lambda x: x + 2
addtwo. __doc__ = """Add 2 to a number.
    >>> addtwo(2)
    4
    >>> addtwo(2.2)
    4.2
    >>> addtwo(3) # Should fail
    6
    """

if __name__ == '__main__':
    import doctest
    doctest.testmod(verbose=True)

Тест doctest в комментарии к функции lambda addtwo() описывает те же тесты, что и в предыдущем разделе.

Когда вы выполняете тесты с помощью doctest.testmod(), вы получаете следующее:

$ python lambda_doctest.py
Trying:
    addtwo(2)
Expecting:
    4
ok
Trying:
    addtwo(2.2)
Expecting:
    4.2
ok
Trying:
    addtwo(3) # Should fail
Expecting:
    6
**********************************************************************
File "lambda_doctest.py", line 16, in __main__.addtwo
Failed example:
    addtwo(3) # Should fail
Expected:
    6
Got:
    5
1 items had no tests:
    __main__
**********************************************************************
1 items had failures:
   1 of   3 in __main__. addtwo
3 tests in 2 items.
2 passed and 1 failed.
***Test Failed*** 1 failures.

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

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

Злоупотребления лямбда-выражениями

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

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

Следующие разделы иллюстрируют несколько примеров использования лямбды, которых следует избегать. Такими примерами могут быть ситуации, когда в контексте лямбда-кода Python код демонстрирует следующий шаблон:

  • Он не следует руководству по стилю Python (PEP 8)
  • Код выглядит громоздким и трудно читаемым.

Возникновение исключения

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

>>> def throw(ex): raise ex
>>> (lambda: throw(Exception('Something bad happened')))()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 1, in <lambda>
    File "<stdin>", line 1, in throw
Exception: Something bad happened

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

Загадочный стиль

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

Следующий лямбда-пример содержит несколько неудачных стилей:

>>> (lambda _: list(map(lambda _: _ // 2, _)))([1,2,3,4,5,6,7,8,9,10])
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]

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

>>> (lambda some_list: list(map(lambda n: n // 2,
                                some_list)))([1,2,3,4,5,6,7,8,9,10])
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]

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

>>> def div_items(some_list):
      div_by_two = lambda n: n // 2
      return map(div_by_two, some_list)
>>> list(div_items([1,2,3,4,5,6,7,8,9,10])))
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]

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

Классы Python

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

class Car:
    """Car with methods as lambda functions."""
    def __init__(self, brand, year):
        self.brand = brand
        self. year = year

    brand = property(lambda self: getattr(self, '_brand'),
                     lambda self, value: setattr(self, '_brand', value))

    year = property(lambda self: getattr(self, '_year'),
                    lambda self, value: setattr(self, '_year', value))

    __str__ = lambda self: f'{self.brand} {self.year}'  # 1: error E731

    honk = lambda self: print('Honk!')     # 2: error E731

При запуске такого инструмента, как flake8, инструмент обеспечения соблюдения стилей, будут отображаться следующие ошибки для __str__ и honk:

E731 do not assign a lambda expression, use a def

Хотя flake8 не указывает на проблему использования лямбда-функций в свойствах, их трудно читать и они подвержены ошибкам из-за использования нескольких строк, таких как _brand и _year.

Ожидается, что правильная реализация __str__ будет выглядеть следующим образом:

def __str__(self):
    return f'{self.brand} {self.year}'

brand будет написана следующим образом:

@property
def brand(self):
    return self._brand

@brand.setter
def brand(self, value):
    self._brand = value

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

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

Лямбды в Python, как правило, являются предметом споров. Некоторые аргументы против лямбды в Python:

  • Проблемы с читабельностью
  • Наложение функционального мышления
  • Тяжелый синтаксис с ключевым словом lambda

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

Следующие примеры иллюстрируют сценарии, в которых использование лямбда-функций не только подходит, но и поощряется в коде Python.

Классические функциональные конструкции

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

>>> list(map(lambda x: x. upper(), ['cat', 'dog', 'cow']))
['CAT', 'DOG', 'COW']
>>> list(filter(lambda x: 'o' in x, ['cat', 'dog', 'cow']))
['dog', 'cow']
>>> from functools import reduce
>>> reduce(lambda acc, x: f'{acc} | {x}', ['cat', 'dog', 'cow'])
'cat | dog | cow'

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

Ключевые функции

Ключевые функции в Python — это функции высшего порядка, которые принимают ключ параметра в качестве именованного аргумента. Ключ получает функцию, которая может быть лямбда-выражением. Эта функция напрямую влияет на алгоритм, управляемый самой ключевой функцией. Вот некоторые ключевые функции:

  • sort(): метод списка
  • sorted()min()max(): встроенные функции
  • nlargest() and nsmallest(): в модуле алгоритма очереди кучи heapq

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

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

>>> ids = ['id1', 'id2', 'id30', 'id3', 'id22', 'id100']
>>> print(sorted(ids)) # Lexicographic sort
['id1', 'id2', 'id30', 'id3', 'id22', 'id100']
>>> sorted_ids = sorted(ids, key=lambda x: int(x[2:])) # Integer sort
>>> print(sorted_ids)
['id1', 'id2', 'id3', 'id22', 'id30', 'id100']

UI Фреймворки

UI фреймворки, такие как Tkinter, wxPython или . NET Windows Forms с IronPython, используют лямбда-функции для отображения действий в ответ на события пользовательского интерфейса.

Простая программа Tkinter, представленная ниже, демонстрирует использование лямбды, назначенной команде кнопки Reverse:

import tkinter as tk
import sys

window = tk.Tk()
window.grid_columnconfigure(0, weight=1)
window.title("Lambda")
window.geometry("300x100")
label = tk.Label(window, text="Lambda Calculus")
label.grid(column=0, row=0)
button = tk.Button(
    window,
    text="Reverse",
    command=lambda: label.configure(text=label.cget("text")[::-1]),
)
button.grid(column=0, row=1)
window.mainloop()

Нажатие кнопки «Reverse» запускает событие, которое запускает лямбда-функцию, изменяя метку с Lambda Calculus на suluclaC adbmaL *:

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

Интерпритатор Python

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

timeit

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

>>> from timeit import timeit
>>> timeit("factorial(999)", "from math import factorial", number=10)
0. 0013087529951008037

Когда инструкция передается в виде строки, timeit() нужен полный контекст. В приведенном выше примере это обеспечивается вторым аргументом, который устанавливает среду, необходимую основной функции для синхронизации. В противном случае возникнет исключение NameError.

Другой подход — использовать лямбду:

>>> from math import factorial
>>> timeit(lambda: factorial(999), number=10)
0.0012704220062005334

Это решение чище, более читабельно и быстрее вводится в интерпретаторе.

Monkey Patching

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

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

from contextlib import contextmanager
import secrets

def gen_token():
    """Generate a random token."""
    return f'TOKEN_{secrets.token_hex(8)}'

@contextmanager
def mock_token():
    """Context manager to monkey patch the secrets.token_hex
    function during testing.
    """
    default_token_hex = secrets.token_hex
    secrets.token_hex = lambda _: 'feedfacecafebeef'
    yield
    secrets.token_hex = default_token_hex

def test_gen_key():
    """Test the random token."""
    with mock_token():
        assert gen_token() == f"TOKEN_{'feedfacecafebeef'}"

test_gen_key()

Диспетчер контекста помогает изолировать операцию monkey patching функцию из стандартной библиотеки (в этом примере secrets). Лямбда назначенная для secrets.token_hex (), заменяет поведение по умолчанию, возвращая статическое значение.

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

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

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

import secrets

def gen_token():
    return f'TOKEN_{secrets. token_hex(8)}'

def test_gen_key(monkeypatch):
    monkeypatch.setattr('secrets.token_hex', lambda _: 'feedfacecafebeef')
    assert gen_token() == f"TOKEN_{'feedfacecafebeef'}"

С помощью pytest secretts.token_hex() перезаписывается лямбда-выражением, которое будет возвращать детерминированное значение feedfacecafebeef, позволяющее подтвердить правильность теста. monkeypatch позволяет вам контролировать область переопределения. В приведенном выше примере при вызове secretts.token_hex() в последующих тестах без использования monkey patching будет выполняться обычная реализация этой функции.

Выполнение теста pytest дает следующий результат:

$ pytest test_token.py -v
============================= test session starts ==============================
platform linux -- Python 3. 7.2, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
cachedir: .pytest_cache
rootdir: /home/andre/AB/tools/bpython, inifile:
collected 1 item

test_token.py::test_gen_key PASSED                                       [100%]

=========================== 1 passed in 0.01 seconds ===========================

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

Альтернативы лямбдам

Хотя существуют веские причины для использования лямбды, есть случаи, когда ее использование не одобряется. Так каковы альтернативы?

Функции более высокого порядка, такие как map(), filter() и functools.reduce(), могут быть преобразованы в более элегантные формы с небольшими изменениями, в частности, со списком или генератором выражений.

Map

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

map() возвращает итератор, соответствующий преобразованной коллекции. Например, если вы хотите преобразовать список строк в новый список с заглавными буквами, вы можете использовать map() следующим образом:

>>> list(map(lambda x: x.capitalize(), ['cat', 'dog', 'cow']))
['Cat', 'Dog', 'Cow']

Вам необходимо вызвать list() для преобразования итератора, возвращаемого map(), в расширенный список, который можно отобразить в интерпретаторе оболочки Python.

Использование генератора списка исключает необходимость определения и вызова лямбда-функции:

>>> [x. capitalize() for x in ['cat', 'dog', 'cow']]
['Cat', 'Dog', 'Cow']

Filter

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

>>> even = lambda x: x%2 == 0
>>> list(filter(even, range(11)))
[0, 2, 4, 6, 8, 10]

Обратите внимание, что filter() возвращает итератор, поэтому необходимо вызывать list, который создает список с заданным итератором.

Реализация, использующая конструкцию генератора списка, дает следующее:

>>> [x for x in range(11) if x%2 == 0]
[0, 2, 4, 6, 8, 10]

Reduce

Начиная с Python 3, Reduce() превратился из встроенной функции в функцию модуля functools. Что касается map() и filter(), его первые два аргумента являются соответственно функцией и итерируемым списком. Он также может принимать инициализатор в качестве третьего аргумента, который используется в качестве начального значения результирующего аккумулятора. Для каждого итерируемого элемента reduce() применяет функцию и накапливает результат, который возвращается, когда итерация исчерпана.

Чтобы применить reduce() к списку пар и вычислить сумму первого элемента каждой пары, вы можете написать так:

>>> import functools
>>> pairs = [(1, 'a'), (2, 'b'), (3, 'c')]
>>> functools. reduce(lambda acc, pair: acc + pair[0], pairs, 0)
6

Более идиоматический подход, использующий выражение генератора в качестве аргумента для sum() в следующем примере:

>>> pairs = [(1, 'a'), (2, 'b'), (3, 'c')]
>>> sum(x[0] for x in pairs)
6

Немного другое и, возможно, более чистое решение устраняет необходимость явного доступа к первому элементу пары и вместо этого использует распаковку:

>>> pairs = [(1, 'a'), (2, 'b'), (3, 'c')]
>>> sum(x for x, _ in pairs)
6

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

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

Лямбда — это питон или нет?

PEP 8, который является руководством по стилю для кода Python, гласит:

Всегда используйте оператор def вместо оператора присваивания, который связывает лямбду непосредственно с идентификатором. (Источник)

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

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

Заключение

Теперь вы знаете, как использовать лямбды в Python и можете:

  • Написать лямбду и использовать анонимные функции
  • Мудро выбирать между лямбдами или обычными функциями
  • Избегать чрезмерного использования лямбд
  • Использовать лямбды с функциями высшего порядка или ключевыми функциями Python

Если у вас есть склонность к математике, вы можете повеселиться, исследуя увлекательный мир лямбда-исчисления (lambda calculus).

Python лямбды подобны соли. Щепотка соли улучшит вкус, но слишком много испортит блюдо.

Оригинальная статья:  Andre Burgaud  How to Use Python lambda Functions

Была ли вам полезна эта статья?

[15 / 4.7]

Python-функций (def): определение с примерами

Что такое функция в Python?

В Python функция — это группа связанных операторов, выполняющих определенную задачу.

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

Кроме того, он позволяет избежать повторения и позволяет многократно использовать код.

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

def имя_функции (параметры):
"" "строка документации" ""
выписка (а) 

Выше показано определение функции, которое состоит из следующих компонентов.

  1. Ключевое слово def , которое отмечает начало заголовка функции.
  2. Имя функции, однозначно идентифицирующее функцию. Именование функций следует тем же правилам написания идентификаторов в Python.
  3. Параметры (аргументы), через которые мы передаем значения функции. Они не являются обязательными.
  4. Двоеточие (:) для обозначения конца заголовка функции.
  5. Необязательная строка документации (docstring), описывающая, что делает функция.
  6. Один или несколько допустимых операторов Python, составляющих тело функции. Заявления должны иметь одинаковый уровень отступа (обычно 4 пробела).
  7. Необязательный return оператор для возврата значения из функции.

Пример функции

  def greet (имя):
    "" "
    Эта функция приветствует
    человек прошел как
    параметр
    "" "
    print («Привет,» + имя + «. Доброе утро!»)  

Как вызвать функцию в Python?

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

  >>> привет ('Павел')
Привет, Пол. Доброе утро!  

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

  def greet (имя):
    "" "
    Эта функция приветствует
    человек прошел как
    параметр
    "" "
    print ("Привет," + имя + ".  Доброе утро!")

привет ('Пол')  

Строки документов

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

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

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

Например :

Попробуйте запустить в оболочке Python следующую команду, чтобы увидеть результат.

  >>> печать (привет .__ doc__)

    Эта функция приветствует
    человек прошел как
    параметр  

Чтобы узнать больше о строках документации в Python, посетите Python Docstrings.


Отчет о возврате

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

Синтаксис возврата

возврат [список_выражений] 

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

Например:

  >>> print (привет («май»))
Привет, май. Доброе утро!
Нет  

Здесь Нет — это возвращаемое значение, поскольку greet () напрямую печатает имя, а оператор return не используется.


Пример возврата

  def absolute_value (число):
    "" "Эта функция возвращает абсолютное
    значение введенного числа "" "

    если число> = 0:
        вернуть номер
    еще:
        return -num


печать (абсолютное_значение (2))

печать (абсолютное_значение (-4))  

Выход

  2
4  

Как функция работает в Python?

Работа функций в Python

Объем и время жизни переменных

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

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

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

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

  def my_func ():
х = 10
print ("Значение внутри функции:", x)

х = 20
my_func ()
print ("Значение вне функции:", x)  

Выход

  Значение внутри функции: 10
Значение вне функции: 20  

Здесь мы видим, что значение x изначально равно 20. Даже несмотря на то, что функция my_func () изменила значение x на 10, это не повлияло на значение вне функции.

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

С другой стороны, переменные вне функции видны изнутри. У них глобальный размах.

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


Типы функций

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

  1. Встроенные функции — Функции, встроенные в Python.
  2. Пользовательские функции — Функции, определяемые самими пользователями.

Учебное пособие по Python: функции

Предыдущая глава: Мелкое и глубокое копирование
Следующая глава: Рекурсия и рекурсивные функции

Функции

Синтаксис

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

Функция в Python определяется оператором def. Общий синтаксис выглядит как это:

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

Тела функций могут содержать оператор возврата. Это может быть где угодно в функции тело. Этот оператор завершает выполнение вызова функции и «возвращает» результат, то есть значение выражения, следующего за ключевым словом return, вызывающему. Если в коде функции нет оператора возврата, функция завершается, когда поток управления достигает конца тела функции.
Пример:

>>> def add (x, y):
... "" "Вернуть x плюс y" ""
... вернуть x + y
...
>>>
 
В следующем интерактивном сеансе функция, которую мы ранее определили будет называться:
>>> добавить (4,5)
9
>>> добавить (8,3)
11
>>>
 

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

>>> def add (x, y = 5):
... "" "Вернуть x плюс y, необязательно" ""
... вернуть x + y
...
>>>
 
Вызов этой функции может выглядеть так:
>>> добавить (4)
9
>>> добавить (8,3)
11
>>>
 

Строка документации

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

>>> execfile ("function1.py")
>>> добавить .__ doc__
'Возвращает x плюс y'
>>> add2 .__ doc__
'Возвращает x плюс y, необязательно'
>>>
 

Параметры ключевого слова

Использование параметров ключевого слова — это альтернативный способ выполнения вызовов функций. Определение функции не меняется.
Пример:

def sumsub (a, b, c = 0, d = 0):
    вернуть a - b + c - d
 
Ключевыми параметрами могут быть только те, которые не используются в качестве позиционных аргументов.
>>> execfile ("funktion1.py")
>>> sumsub (12,4)
8
>>> sumsub (12,4,27,23)
12
>>> sumsub (12,4, d = 27, c = 23)
4
 

Произвольное количество параметров

В программировании существует множество ситуаций, в которых точное количество необходимых параметры не могут быть определены априори. Произвольный номер параметра может быть выполняется на Python с так называемыми ссылками на кортежи. Звездочка «*» используется перед последнего имени параметра, чтобы обозначить его как ссылку на кортеж.Эту звездочку не следует путать с синтаксисом C, где эта нотация связана с указателями.
Пример:

def произвольно (x, y, * подробнее):
    напечатать "x =", x, ", y =", y
    напечатать «произвольно:», подробнее
 
x и y — обычные позиционные параметры в предыдущей функции. * больше ссылка на кортеж.
Пример:
>>> execfile ("funktion1.py")
>>> произвольно (3,4)
х = 3, х = 4
произвольный:  ()
>>> произвольно (3,4, «Hello World», 3, 4)
х = 3, х = 4
произвольно: ('Hello World', 3, 4)
 
Предыдущая глава: Мелкое и глубокое копирование
Следующая глава: Рекурсивные и рекурсивные функции

5.Функции — начало программирования на Python для начинающих веб-разработчиков

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

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

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

5.1. Определение функций и использование

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

 def НАЗВАНИЕ (СПИСОК ПАРАМЕТРОВ):
    ЗАЯВЛЕНИЯ
 

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

Внутри функции может быть любое количество операторов, но они должны быть отступ от def .

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

  1. Заголовок , который начинается с ключевого слова и заканчивается двоеточием.

  2. Тело , состоящее из одного или нескольких операторов Python, каждый с отступом такое же количество ( 4 пробела — стандарт Python ) из ​​заголовка.

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

5.2. Основываясь на том, что вы узнали в старшей школе по алгебре

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

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

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

Следующие квадратичная функция — это пример:

Вот такая же функция в Python:

 def f (x):
    возврат 3 * x ** 2 - 2 * x + 5
 

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

Вот наша функция f , вызываемая с несколькими разными аргументами:

 >>> f (3)
26
>>> f (0)
5
>>> f (1)
6
>>> f (-1)
10
>>> f (5)
70
 

Определение функции должно сначала быть введено в оболочку Python перед этим можно позвонить:

 >>> def f (x):
... вернуть 3 * x ** 2 - 2 * x + 5
...
>>>
 

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

Связь между параметром и аргументом в определении и вызов функции — это неявное присвоение . Это как если бы мы выполнили операторы присваивания x = 3 , x = 0 , x = 1 , x = -1 и x = 5 соответственно перед вызовом функции на f в предыдущем примере.

5.3.

возврат выписка

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

 >>> результат = f (3)
>>> результат
26
>>> результат = f (3) + f (-1)
>>> результат
36
 

Оператор возврата без значения после того, как он все еще возвращает значение типа мы не видели раньше:

 >>> def mystery ():
...    возвращаться
...
>>> what_is_it = тайна ()
>>> what_is_it
>>> тип (what_is_it)
<класс 'NoneType'>
>>> print (what_is_it)
Никто
 

None является единственным значением Python NoneType . Мы будем использовать это часто позже для представления неизвестного или неназначенного значения. А пока тебе нужно быть знайте, что это значение возвращается оператором return без Аргумент.

Все вызовы функций Python возвращают значение. Если вызов функции завершается выполнение операторов в своем теле без нажатия return , a Нет Значение не возвращается из функции.

 >>> def do_nothing_useful (n, m):
... х = п + м
... y = n - m
...
>>> do_nothing_useful (5, 3)
>>> результат = do_nothing_useful (5, 3)
>>> результат
>>>
>>> print (результат)
Никто
 

Поскольку do_nothing_useful не имеет оператора возврата со значением, он возвращает значение None , которое присваивается результату . Нет значений не отображаются в оболочке Python, если они явно не напечатаны.

Любые операторы в теле функции после return обнаруженные никогда не будут выполнены и упоминаются как мертвый код.

 >>> def try_to_print_dead_code ():
... print ("Это напечатает ...")
... print ("... и это будет.")
...    возвращаться
... print ("Но не это ...")
... print ("потому что это мертвый код!")
...
>>> try_to_print_dead_code ()
Это напечатает ...
... и так будет.
>>>
 

5.4. Поток исполнения

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

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

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

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

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

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

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

 def f1 ():
    print ("Мо")

def f2 ():
    f4 ()
    print ("Минни")

def f3 ():
    f2 ()
    print ("Miny")
    f1 ()

def f4 ():
    print ("Ини")

f3 ()
 

Результат этой программы:

Проследите за процессом выполнения и посмотрите, сможете ли вы понять, почему он это делает.

5.5. Инкапсуляция и обобщение

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

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

Чтобы увидеть, как работает этот процесс, давайте начнем с программы, которая считает количество цифр в номере 4203 :

Номер
 = 4203
count = 0

а число! = 0:
    count + = 1
    число // = 10

печать (количество)
 

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

Первый шаг в инкапсуляции этой логики — заключить ее в функцию:

 def num_digits ():
    число = 4203
    count = 0

    а число! = 0:
        count + = 1
        число // = 10

    счетчик возврата

печать (число_цифров ())
 

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

 def num_digits (число):
    count = 0

    а число! = 0:
        count + = 1
        число // = 10

    счетчик возврата

печать (число_цифров (4203))
 

После того, как параметризует значение , мы теперь можем использовать нашу логику для подсчета цифр любое положительное целое число. При вызове print (num_digits (710)) будет напечатано 3 .Вызов print (num_digits (1345109)) напечатает 7 и так далее.

Эта функция также содержит ошибки. Если мы вызовем num_digits (0) , он вернет 0 , когда он должен вернуть 1 . Если мы вызовем num_digits (-23) , программа переходит в бесконечный цикл. Вам будет предложено исправить обе эти ошибки. как упражнение.

5.6. Композиция

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

 >>> def f (x):
... вернуть 2 * x
...
>>> def g (x):
... вернуть x + 5
...
>>> def h (x):
... вернуть x ** 2 - 3
>>> f (3)
6
>>> г (3)
8
>>> ч (4)
13
>>> f (g (3))
16
>>> g (f (3))
11
>>> h (f (g (0)))
97
>>>
 

Мы также можем использовать переменную в качестве аргумента:

 >>> # Предположим определения функций для f и g, как в предыдущем примере
>>> val = 10
>>> f (val)
20
>>> f (g (val))
30
>>>
 

Обратите внимание на кое-что очень важное. Имя переменной, которую мы передаем как аргумент ( val ) не имеет ничего общего с именем параметра ( x ). Опять же, это как если бы x = val выполняется, когда вызывается f (val) . Это не имеет значения, какое значение было названо у вызывающего абонента, внутри f и g его имя x .

5.7. Функции тоже данные

Функции, которые вы определяете в Python, являются типом данных.

 >>> def f ():
... print ("Привет из функции f!")
...
>>> тип (f)
<тип 'функция'>
>>> f ()
Привет, из функции f!
>>>
 

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

 >>> do_stuff = [f, g, h]
>>> для func в do_stuff:
... функция (10)
...
20
15
97
 

Как обычно, вы должны отслеживать выполнение этого примера, пока не почувствуете уверены, что понимаете, как это работает.

5.8. Список параметров

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

 def double_stuff_v1 (a_list):
    индекс = 0
    для значения в a_list:
        a_list [индекс] = 2 * значение
        индекс + = 1
 

Чтобы проверить эту функцию, мы поместим ее в файл с именем pure_v_modify.py , и импортирует в нашу оболочку Python, где мы можем поэкспериментировать:

 >>> from pure_v_modify import double_stuff_v1
>>> things = [2, 5, 'Спам', 9.5]
>>> double_stuff_v1 (вещи)
>>> вещи
[4, 10, 'SpamSpam', 19.0]
 

Примечание

Файл, содержащий импортированный код, должен иметь .py расширение файла, которое не записано в заявлении об импорте .

Параметр a_list и переменная things являются псевдонимами для одного и того же объект.Диаграмма состояний выглядит так:

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

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

5.9. Чистые функции и модификаторы

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

Чистая функция не вызывает побочных эффектов.Он общается с вызывающая программа только через параметры, которые она не изменяет, и возврат ценить. Вот double_stuff_v2 , записанная как чистая функция:

 def double_stuff_v2 (a_list):
    new_list = []
    для значения в a_list:
        новый_лист + = [2 * значение]
    вернуть новый_лист
 

Эта версия double_stuff не изменяет свои аргументы:

 >>> from pure_v_modify import double_stuff_v2
>>> things = [2, 5, 'Спам', 9. 5]
>>> double_stuff_v2 (вещи)
[4, 10, 'SpamSpam', 19.0]
>>> вещи
[2, 5, "Спам", 9.5]
>>>
 

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

 >>> вещи = double_stuff (вещи)
>>> вещи
[4, 10, 'SpamSpam', 19.0]
>>>
 

5.10. Что лучше?

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

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

5.11. Полиморфизм и типирование уток

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

 >>> def double (вещь):
... вернуть 2 * вещь
...
>>> двойной (5)
10
>>> double ('Спам')
'СпамСпам'
>>> double ([1, 2])
[1, 2, 1, 2]
>>> двойной (3,5)
7.0
>>> double (('а', 'б'))
('а', 'б', 'а', 'б')
>>> двойной (Нет)
Отслеживание (последний вызов последний):
  Файл "", строка 1, в 
  Файл "", строка 2, в двойном формате
TypeError: неподдерживаемые типы операндов для *: 'int' и 'NoneType'
>>>
 

Поскольку * определено для целых чисел, строк, списков, чисел с плавающей запятой и кортежей, вызов нашей функции double с любым из этих типов в качестве аргумента не проблема. * не определен для NoneType, поэтому отправка double function a None Значение приводит к ошибке времени выполнения.

5.12. Двумерные столы

Двумерная таблица — это таблица, в которой вы читаете значение на пересечении строки и столбца. Таблица умножения — хороший пример. Скажем вы хотите распечатать таблицу умножения для значений от 1 до 6.

Хороший способ начать — написать цикл, который печатает числа, кратные 2, все на одна строка:

 для i в диапазоне (1, 7):
    print (2 * i, end = "")
Распечатать()
 

Здесь мы использовали функцию range , но заставили ее начать свою последовательность с 1.По мере выполнения цикла значение i изменяется с 1 на 6. Когда все элементам диапазона присвоены номера и , цикл завершается. Каждый раз в цикле отображается значение 2 * i , за которым следуют три пробелы.

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

Вывод программы:

Пока все хорошо. Следующий шаг — инкапсулировать и обобщить .

5.13. Больше инкапсуляции

Эта функция инкапсулирует предыдущий цикл и обобщает его для печати кратные n :

 def print_multiples (n):
    для i в диапазоне (1, 7):
        print (n * i, end = "")
    Распечатать()
 

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

Если мы вызовем эту функцию с аргументом 2, мы получим тот же результат, что и раньше. С аргументом 3 вывод:

С аргументом 4 вывод:

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

 для i в диапазоне (1, 7):
    print_multiples (я)
 

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

Результатом этой программы является таблица умножения:

 1 2 3 4 5 6
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
5 10 15 20 25 30
6 12 18 24 30 36
 

5.14. Еще больше инкапсуляции

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

 def print_mult_table ():
    для i в диапазоне (1, 7):
        print_multiples (я)
 

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

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

5.15. Локальные переменные

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

Ответ — нет, потому что i в print_multiples и i в print_mult_table — это , а не , одна и та же переменная.

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

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

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

Значение i в print_mult_table изменяется от 1 до 6. На диаграмме это оказывается 3.При следующем прохождении цикла это будет 4. Каждый раз через цикл print_mult_table вызывает print_multiples с текущим значением и в качестве аргумента. Это значение присваивается параметру n .

Внутри print_multiples значение i изменяется от 1 до 6. В Диаграмма, оказывается 2. Изменение этой переменной не влияет на значение из и в print_mult_table .

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

5.16. Рекурсивные структуры данных

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

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

Вложенный список номеров — это список, элементы которого:

  1. номера

  2. списки вложенных номеров

Обратите внимание, что термин «вложенный список номеров» используется в собственном определении. Рекурсивные определения такие довольно распространены в математике и Информатика. Они позволяют кратко и эффективно описать рекурсивные структуры данных которые частично состоят из более мелких и простых экземпляров самих себя. В определение не является круговым, поскольку в какой-то момент мы достигнем списка, который не иметь списков в качестве элементов.

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

 >>> сумма ([1, 2, 8])
11
>>> sum ((3, 5, 8.5))
16,5
>>>
 

Однако для нашего списка вложенных номеров , сумма не будет работать:

 >>> сумма ([1, 2, [11, 13], 8])
Отслеживание (последний вызов последний):
  Файл "", строка 1, в 
TypeError: неподдерживаемые типы операндов для +: 'int' и 'list'
>>>
 

Проблема в том, что третий элемент этого списка, [11, 13] , сам является список, который нельзя добавить в 1 , 2 и 8 .

5.17. Рекурсия

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

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

 def recursive_sum (nested_num_list):
    the_sum = 0
    для элемента в nested_num_list:
        если type (element) == list:
            the_sum = the_sum + recursive_sum (элемент)
        еще:
            the_sum = the_sum + элемент
    вернуть the_sum
 

Тело recursive_sum состоит в основном из цикла для , который проходит nested_num_list . Если элемент является числовым значением (ветвь иначе ), он просто добавляется к the_sum . Если элемент является списком, то recursive_sum вызывается снова с элементом в качестве аргумента. В оператор внутри определения функции, в котором функция вызывает себя, является известный как рекурсивный вызов.

Recursion — действительно один из самых красивых и элегантных инструментов на компьютере. наука.

Немного более сложная проблема — найти наибольшее значение в нашем вложенном список номеров:

 def recursive_max (nested_num_list):
    "" "
      >>> recursive_max ([2, 9, [1, 13], 8, 6])
      13
      >>> recursive_max ([2, [[100, 7], 90], [1, 13], 8, 6])
      100
      >>> recursive_max ([2, [[13, 7], 90], [1, 100], 8, 6])
      100
      >>> recursive_max ([[[13, 7], 90], 2, [1, 100], 8, 6])
      100
    "" "
    наибольший = nested_num_list [0]
    а тип (наибольший) == тип ([]):
        наибольший = наибольший [0]

    для элемента в nested_num_list:
        если type (element) == type ([]):
            max_of_elem = recursive_max (элемент)
            если самый большой 

Доктесты включены, чтобы предоставить примеры работы recursive_max .

Дополнительным поворотом к этой проблеме является поиск числового значения для инициализации наибольший . Мы не можем просто использовать nested_num_list [0] , так как я либо число или список. Чтобы решить эту проблему, мы используем цикл while, который назначает самое большое до первого числового значения независимо от того, насколько глубоко оно вложено.

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

Запишите следующее в файл с именем infinite_recursion.py :

 #
# infinite_recursion.py
#
def recursion_depth (число):
    print "Число глубины рекурсии% d." % номер
    recursion_depth (число + 1)

рекурсия_глубина (0)
 

В командной строке unix в том же каталоге, в котором вы сохранили программа введите следующее:

 python infinite_recursion. ру
 

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

 ...
  Файл infinite_recursion.py, строка 3, в recursion_depth
    recursion_depth (число + 1)
RuntimeError: превышена максимальная глубина рекурсии
 

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

5.18. Исключения

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

Например, деление на ноль создает исключение:

 >>> печать 55/0
Отслеживание (последний вызов последний):
  Файл "", строка 1, в 
ZeroDivisionError: целочисленное деление или по модулю нуля
>>>
 

То же самое и с доступом к несуществующему элементу списка:

 >>> a = []
>>> выведите [5]
Отслеживание (последний вызов последний):
  Файл "", строка 1, в 
IndexError: список индекса вне допустимого диапазона
>>>
 

Или попытка присвоения элемента кортежу:

 >>> tup = ('а', 'б', 'д', 'д')
>>> tup [2] = 'c'
Отслеживание (последний вызов последний):
  Файл "", строка 1, в 
TypeError: объект 'tuple' не поддерживает назначение элементов
>>>
 

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

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

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

 filename = raw_input ('Введите имя файла:')
пытаться:
    f = open (имя файла, "r")
Кроме:
    print 'Нет файла с именем', имя файла
 

Оператор try выполняет операторы в первом блоке.Если нет возникают исключения, он игнорирует , кроме оператора . Если возникает какое-либо исключение, он выполняет операторы в , кроме ветви , а затем продолжает.

Мы можем инкапсулировать эту возможность в функции: существует принимает имя файла и возвращает true, если файл существует, false, если его нет:

 def существует (имя файла):
    пытаться:
        f = open (имя файла)
        f. close ()
        вернуть True
    Кроме:
        return False
 

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

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

 #
# learn_exceptions.py
#
def get_age ():
    age = input ('Пожалуйста, введите свой возраст:')
    если возраст <0:
        Raise ValueError, "% s не является допустимым возрастом"% age
    возраст возвращения
 

Оператор raise принимает два аргумента: тип исключения и конкретный информация об ошибке. ValueError - это встроенное исключение, которое наиболее точно соответствует той ошибке, которую мы хотим вызвать. Полный список встроенных исключений находится в разделе «Встроенные исключения» Python Справочник по библиотеке, опять же создателя Python, Гвидо ван Россум.

Если функция, вызвавшая get_age , обрабатывает ошибку, то программа может Продолжить; в противном случае Python печатает трассировку и завершает работу:

 >>> get_age ()
Пожалуйста, введите ваш возраст: 42
42
>>> get_age ()
Пожалуйста, введите ваш возраст: -2
Отслеживание (последний вызов последний):
  Файл "", строка 1, в 
  Файл "learn_exceptions.py ", строка 4, в get_age
    Raise ValueError, "% s не является допустимым возрастом"% age
ValueError: -2 не является допустимым возрастом
>>>
 

Сообщение об ошибке включает тип исключения и дополнительную информацию. вы предоставили.

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

 #
# infinite_recursion.py
#
def recursion_depth (число):
    print "Число глубины рекурсии% d." % номер
    пытаться:
        recursion_depth (число + 1)
    Кроме:
        print "Превышена максимальная глубина рекурсии. "

рекурсия_глубина (0)
 

Запустите эту версию и посмотрите на результаты.

5.19. Хвостовая рекурсия

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

Вот версия функции обратного отсчета из главы 6, написанная с использованием хвостовая рекурсия:

 def обратный отсчет (n):
    если n == 0:
        печать "Blastoff!"
    еще:
        напечатать n
        обратный отсчет (n-1)
 

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

 def find_max (seq, max_so_far):
    если не seq:
        вернуть max_so_far
    если max_so_far 

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

5.20. Рекурсивные математические функции

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

Мы можем легко запрограммировать это на Python:

 def factorial (n):
    если n == 0:
        возврат 1
    еще:
        вернуть n * факториал (n-1)
 

Еще одно хорошо известное рекурсивное соотношение в математике - это соотношение Фибоначчи. последовательность, которая определяется по:

 фибоначчи (0) = 1
фибоначчи (1) = 1
фибоначчи (п) = фибоначчи (п-1) + фибоначчи (п-2)
 

Это также можно легко написать на Python:

 def fibonacci (n):
    если n == 0 или n == 1:
        возврат 1
    еще:
        вернуть фибоначчи (n-1) + fibonacci (n-2)
 

Вызов факториала (1000) превысит максимальную глубину рекурсии.И попробуйте запустите fibonacci (35) и посмотрите, сколько времени потребуется для завершения (будьте терпеливы, это завершу).

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

5.21. Глоссарий

аргумент

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

поток выполнения

Порядок, в котором операторы выполняются во время выполнения программы.

frame

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

функция

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

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

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

композиция функций

Использование выходных данных одного вызова функции в качестве входных данных для другого.

определение функции

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

Первая часть составного отчета. Заголовки начинаются с ключевого слова и заканчиваться двоеточием (:)

локальная переменная

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

Нет

Единственное значение . Нет часто используется для представляют собой отсутствие значения. Также возвращается return оператор без аргументов или функция, которая достигает конца своего body без нажатия return statement, содержащего значение.

параметр

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

диаграмма стека

Графическое представление стека функций, их переменных, и ценности, к которым они относятся.

трассировка

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

7.5 Определения функций

7.5 Определения функций

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

funcdef: "def" funcname "(" [список_параметров] ")" ":" набор
список_параметров: (defparameter ",") * ("*" идентификатор [, "**" идентификатор]
                                    | "**" идентификатор
                                    | defparameter [","])
defparameter: параметр ["=" выражение]
подсписок: параметр ("," параметр) * [","]
параметр: идентификатор | "(" подсписок ")"
funcname: идентификатор
 

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

Определение функции не выполняет тело функции; это получает выполняется только при вызове функции.

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

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

def whats_on_the_telly (penguin = None):
    если пингвин - Нет:
        пингвин = []
    пингвин.append ("собственность зоопарка")
    вернуться пингвин
 

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

Также возможно создание анонимных функций (функции не связаны к имени), для немедленного использования в выражениях. Здесь используются лямбда-формы, описано в разделе. Обратите внимание, что лямбда-форма просто сокращение для упрощенного определения функции; функция определенный в операторе `` def '' может быть передан или присваивается другому имени так же, как функция, определенная лямбда форма. Форма def на самом деле более мощная, так как она позволяет выполнять несколько операторов.

Примечание программиста: форма `` def '', выполняемая внутри определение функции определяет локальную функцию, которая может быть возвращена или прошел вокруг. Из-за философии двух областей видимости Python местный функция, определенная таким образом, не имеет доступа к локальному переменные функции, содержащей ее определение; то же правило применяется к функциям, определенным лямбда-формой. Стандартный трюк передать выбранные локальные переменные в локально определенную функцию, чтобы используйте значения аргументов по умолчанию, например:

# Возвращает функцию, которая возвращает аргумент, увеличенный на 'n'
def make_incrementer (n):
    def приращение (x, n = n):
        вернуть x + n
    приращение возврата

add1 = make_incrementer (1)
print add1 (3) # Это напечатает '4'
 

См. Об этом документе... для получения информации о предложениях изменений.

функций Python (с примерами)

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

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

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

 def имя_функции (параметры):
    "" "строка документации" ""
    заявление1
    заявление2
    ...
    ...
    возврат [выражение]
 

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

Первый оператор в теле функции может быть строкой, которая называется строкой документации . Он объясняет функциональность функции / класса. Строка документации не является обязательной.

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

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

В следующем примере определяется функция greet () .

  def greet ():
    "" "Эта функция отображает" Hello World! "" ""
    print ('Привет, мир!')
  

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

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

По умолчанию все функции возвращают Нет , если оператор return не существует.

Функция help () отображает строку документации, как показано ниже.

  >>> помощь (привет)
Справка по функции greet в модуле __main__:

    приветствовать()
        Эта функция отображает "Hello World!"  

Параметры функции

Можно определить функцию для получения одного или нескольких параметров (также называемых аргументами) и использования их для обработки внутри функционального блока.Параметрам / аргументам можно дать подходящие формальные имена. Функция greet () теперь определена для получения строкового параметра с именем name . Внутри функции оператор print () модифицируется для отображения приветственного сообщения, адресованного полученному параметру.

  def greet (имя):
    print ('Привет', имя)

greet ('Steve') # вызываем функцию с аргументом
приветствовать (123)
  

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

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

  def приветствие (name: str):
    print ('Привет', имя)

greet ('Steve') # вызываем функцию со строковым аргументом
greet (123) # вывести ошибку для аргумента int
  

Несколько параметров

Функция может иметь несколько параметров.Следующая функция принимает три аргумента.

  def greet (имя1, имя2, имя3):
    print ('Привет', имя1, ',', имя2, 'и', имя3)

greet ('Steve', 'Bill', 'Yash') # вызываем функцию со строковым аргументом
  
  Привет, Стив, Билл и Яш
  

Неизвестное количество аргументов

Функция в Python может иметь неизвестное количество аргументов, если перед параметром поставить * , если вы не знаете количество аргументов, которые пользователь собирается передать.

  def привет (* имена):
    print ('Привет', имена [0], ',', имена [1], ',', имена [3])

приветствовать ('Стив', 'Билл', 'Яш')
  
  Привет, Стив, Билл и Яш
  

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

  def привет (* имена):
я = 0
print ('Привет', конец = '')
пока len (имена)> i:
print (имена [i], end = ',')
я + = 1

приветствовать ('Стив', 'Билл', 'Яш')
привет ('Стив', 'Билл', 'Яш', 'Капил', 'Джон', 'Амир')
  
  Привет Стив, Билл, Яш,
Привет, Стив, Билл, Яш, Капил, Джон, Амир
  

Функция с аргументами ключевого слова

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

  def greet (имя, фамилия):
    print ('Привет', имя, фамилия)

greet (lastname = 'Jobs', firstname = 'Steve') # передача параметров в любом порядке с использованием аргумента ключевого слова
  

Аргумент ключевого слова ** kwarg

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

  def привет (** человек):
print ('Привет', человек ['имя'], человек ['фамилия'])

приветствую (firstname = 'Steve', lastname = 'Jobs')
приветствую (lastname = 'Jobs', firstname = 'Steve')
привет (firstname = 'Bill', lastname = 'Gates', возраст = 55)
greet (firstname = 'Bill') # вызывает KeyError
  
  Привет, Стив Джобс
Привет, Стив Джобс
Привет Билл Гейтс
  

При использовании параметра ** порядок аргументов не имеет значения.Однако названия аргументов должны быть такими же. Получите доступ к значению аргументов ключевого слова, используя paramter_name ['keyword_argument'] .

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

  def привет (** человек):
print ('Привет', человек ['имя'], человек ['фамилия'])

greet (firstname = 'Bill') # вызывает ошибку KeyError, необходимо предоставить аргумент 'lastname'
  
  Traceback (последний звонок последний):
  Файл "", строка 1, в 
    приветствовать (firstname = 'Bill')
  Файл "", строка 2, в приветствии
    print ('Привет', человек ['имя'], человек ['фамилия'])
KeyError: "фамилия"
  

Параметр со значением по умолчанию

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

Следующая функция greet () определяется параметром name , имеющим значение по умолчанию 'Guest' . Он будет заменен, только если будет передан какой-либо фактический аргумент.

  def greet (name = 'Гость'):
    print ('Привет', имя)

приветствовать()
приветствую ('Стив')
  

Функция с возвращаемым значением

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

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

  def sum (a, b):
    вернуть a + b
  

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

  итого = сумма (10, 20)
печать (всего)
всего = сумма (5, сумма (10, 20))
печать (всего)
  

Основы функций в Python | Вычислительные методы в гражданской сфере в Стэнфордском университете

Мы использовали функции с самого начала:

  печать («привет, мир»)
  

Маркер print - это , а не - специальное ключевое слово Python.Это имя, присвоенное блоку кода, который выполняет работу по принятию аргумента - «hello world» - и его вывод на экран. Фактический код, который выполняет , который работает , намного сложнее, чем вы, вероятно, думали:

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

  print («привет, мир», «это», «я»)
  

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

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

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

Остальная часть этого руководства посвящена простому синтаксису для определения функций. Ознакомьтесь с главой 3 Эла Свигарта: «Функции» через «Автоматизируйте скучную работу», чтобы получить хорошее пошаговое руководство, охватывающее функции и некоторые из связанных с ними тем.

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

  def hello_world ():
    print ("Привет, мир!")
  

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

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

  >>> hello_world ()
Привет, мир!
  

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

  >>> hello_world
<функция __main__.hello_world>
>>> тип (hello_world)
функция
  

Синтаксические компоненты базовой функции Python

Из документации Python:

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

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

По сути, функцию можно разбить на эту простую форму:

  def the_function_name ():
    what_the_function_does
  

ключевое слово

def

Это лишь одно из тех ключевых слов Python, которые вы запоминаете. Это сокращение от ``

Имя функции

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

Что касается того, какой код может находиться внутри блока кода с отступом; все, что является допустимым кодом Python. Так же, как блоки с отступом для условных переходов и циклов for.

Одним из тонких аспектов функции print является то, что она не возвращает значение . Что это обозначает? Проще продемонстрировать функцию, в которой возвращает значение : len

  >>> x = len ("привет")
  

Переменная x содержит значение 5, потому что функция len предназначена для возврата целого числа, представляющего длину любого значения, переданного ей.

Теперь попробуйте print () :

  >>> pval = print ("привет")
Привет
  

Функция явно выполнена, потому что на экран выводится hello . Но что присвоено переменной pval ? ничего

  >>> тип (pval)
NoneType
  

Ключевое слово возврата

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

  def hello_world_again ():
    return "Привет, мир!"
  

Попробуйте это в интерактивной консоли:

  >>> txt = hello_world_again ()
>>> тип (txt)
ул.
  

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

  >>> печать (hello_world_again ())
Привет, мир!
  

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

  >>> печать (hello_world_again)
<функция hello_world_again at 0x10c289bf8>
  

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

Функция ниже всегда будет просто возвращать "привет" :

  def foo_a ():
    ответь "привет"
    вернуть "мир"
  

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

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

  def hello_there (кто-то):
    print ("Привет,% s!"% str (кто-то) .upper ())
  
  >>> hello_there ('дан')
Привет, ДАН!
>>> hello_there (42 * 42)
Привет, 1764 год!
  

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

Функции могут иметь более одного аргумента; используйте запятую для разделения каждого аргумента:

  def sup (first_name, last_name):
    print ("Sup,% s% s?"% (last_name, first_name))
  
  >>> sup ('дан', 'нгуен')
Sup, nguyen dan?
  

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

  def sup_2 (first_name, last_name = 'Doe'):
    print ("Sup,% s% s?"% (last_name, first_name))
  
  >>> sup_2 ('Джон', "Джонсон")
Суп, Джон Джонсон?
>>> sup_2 ('Джон')
Суп, Джон Доу?
  

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

Например, мы неоднократно получали файлы из Интернета и, в случае файлов JSON, десериализуем их в объекты Python:

  запросов на импорт
импортировать json
resp = requests.get ("http://stash.compciv.org/congress-twitter/2016-01/sentcruz.json")
mydata = json.loads (соответственно текст)
  

Если бы нам пришлось сделать это для многих файлов JSON (требующих загрузки из Интернета), нам пришлось бы копировать и вставлять этот фрагмент кода снова и снова.Это повторяющееся. И это подвержено ошибкам.

Итак, давайте превратим его в функцию. Основная стратегия при разработке функции - знать:

  • Что мне нужно делать?
  • Что должна возвращать функция ?
  • Что я могу аннотация ?

Что мне нужно делать?

Итак, учитывая URL-адрес файла JSON, нам нужно загрузить его и превратить в объект данных. Мы только сделали это выше, поэтому давайте просто скопируем этот код и вставим его в тело функции с именем fetch_json :

  def fetch_json ():
    запросы на импорт
    импортировать json
    resp = запросы.получить ("http://stash.compciv.org/congress-twitter/2016-01/sentcruz.json")
    mydata = json.loads (соответственно текст)
  

Что должна вернуть функция?

Если вы скопируете и вставите этот код выше, а затем вызовете функцию:

  >>> fetch_json ()
  

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

Итак, давайте просто вернем десериализованный объект:

  def fetch_json ():
    запросы на импорт
    импортировать json
    resp = запросы.получить ("http://stash.compciv.org/congress-twitter/2016-01/sentcruz.json")
    mydata = json.loads (соответственно текст)
    вернуть мои данные
  

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

  >>> вещь = fetch_json ()
>>> тип (вещь)
диктовать
>>> вещь ['имя']
'Сенатор Тед Круз'
  

Что я могу абстрагироваться?

Здесь мы думаем о аргументе .Функция fetch_json работает… но только в том случае, если мы хотим продолжать получать спрятанную версию профиля Теда Круза в Twitter.

Итак, какая часть функции является повторяемой . А какая часть функции меняет ?

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

  def fetch_json (url):
    запросы на импорт
    импортировать json
    resp = запросы.получить (URL)
    mydata = json.loads (соответственно текст)
    вернуть мои данные
  

Теперь мы можем указать его на любой URL-адрес, который якобы содержит текст в формате JSON. Попробуйте для себя позвонить fetch_json по этим URL-адресам:

  • http://stash.compciv.org/congress-twitter/2016-01/sensanders.json
  • http://stash.compciv.org/congress-twitter/2016-01/congress-twitter-profiles.json
  • https://status.github.com/api.json
  • https: // status.github.com/api/status.json
  • https://data.usajobs.gov/api/jobs?series=2210

Ознакомьтесь с главой 3 Эла Свигарта: Функции через Automate the Boring Stuff, чтобы получить хорошее пошаговое руководство по функциям и некоторым из связанных с ними тем.

функций Python - AskPython

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

Как определить функцию в Python?

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

def привет ():
    print ('Привет, мир')


def add (x, y):
    print (аргументы f: {x} и {y} ')
    вернуть x + y
 

На основе приведенных выше примеров мы можем определить структуру функции как это.

def имя_функции (аргументы):
    # операторов кода
 
Функции Python

Как вызвать функцию в Python?

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

Привет()
сумма = добавить (10, 5)
print (f'sum равно {sum} ')
 

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


Типы функций Python

В Python есть два типа функций.

  1. встроенные функции: функции, предоставляемые языком Python, такие как print (), len (), str () и т. Д.
  2. пользовательские функции: функции, определенные нами в программе Python.

Может ли функция иметь значение параметра по умолчанию?

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

def привет (год = 2019):
    print (f'Hello World {год} ')


hello (2020) # параметр функции передан
hello () # параметр функции не передан, поэтому будет использоваться значение по умолчанию
 

Выход:

Привет, мир 2020
Привет, мир 2019
 

Можно ли иметь несколько операторов возврата внутри функции?

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

def odd_even_checker (я):
    если я% 2 == 0:
        вернуть 'даже'
    еще:
        вернуть "нечетное"


печать (odd_even_checker (20))
печать (odd_even_checker (15))
 

Может ли функция Python возвращать несколько значений одно за другим?

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

def return_odd_ints (i):
    х = 1
    в то время как x <= i:
        доход х
        х + = 2


output = return_odd_ints (10)
для вывода:
    распечатывать)
 

Выход:

1
3
5
7
9
 

Аргументы переменных функций Python

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

  1. Формальные аргументы: те, что мы уже видели в примерах.
  2. Переменная Число аргументов, не являющихся ключевыми словами: например, def add (* args)
  3. Переменная Число аргументов ключевого слова или именованных аргументов: например, def add (** kwargs)

Некоторые важные моменты относительно аргументов переменных в Python:

  • Порядок аргументов должен быть формальными аргументами, * args и ** kwargs.
  • Не обязательно использовать имена параметров переменных как args и kwargs.Однако лучше всего использовать их для лучшей читаемости кода.
  • Тип аргумента - кортеж. Таким образом, мы можем передать кортеж для отображения с переменной * args.
  • Тип кваргов - дикт. Таким образом, мы можем передать словарь для сопоставления с переменной ** kwargs.

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

def add (x, y, * args, ** kwargs):
    сумма = х + у
    для аргументов:
        сумма + = а

    для k, v в kwargs.items ():
        сумма + = v
    сумма возврата


total = add (1, 2, * (3, 4), ** {"k1": 5, "k2": 6})
печать (всего) # 21
 

Рекурсивная функция Python

Когда функция вызывает саму себя, она называется рекурсивной функцией.Этот сценарий в программировании называется рекурсией.

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

def fibonacci_numbers_at_index (количество):
    если count <= 1:
        счетчик возврата
    еще:
        вернуть fibonacci_numbers_at_index (count - 1) + fibonacci_numbers_at_index (count - 2)


count = 5
я = 1
пока я <= count:
    печать (fibonacci_numbers_at_index (i))
    я + = 1
 

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


Тип данных функции Python

Функции Python являются экземплярами класса «функция». Мы можем проверить это с помощью функции type ().

def foo ():
    проходить


печать (тип (foo))
 

Выход : <класс «функция»>


Функция Python против метода

  • Функция Python является частью файла сценария Python, в котором она определена, тогда как методы определены внутри определения класса.
  • Мы можем вызвать функцию напрямую, если она находится в том же модуле. Если функция определена в другом модуле, мы можем импортировать модуль, а затем напрямую вызвать функцию. Нам нужен класс или объект класса для вызова методов.
  • Функция Python может обращаться ко всем глобальным переменным, тогда как методы класса Python могут обращаться к глобальным переменным, а также к атрибутам и функциям класса.
  • Тип данных функций Python - «функция», тогда как тип данных методов Python - «метод».

Давайте посмотрим на простой пример функций и методов в Python.

Данные класса:
    def foo (сам):
        print ('метод foo')


def foo ():
    print ('функция foo')


# вызов функции
foo ()

# вызов метода
d = Данные ()
d.foo ()

# проверка типов данных
печать (тип (foo))
печать (введите (d.foo))
 

Выход:

функция foo
foo метод
<класс 'функция'>
<класс 'метод'>
 

Преимущества функций Python

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

Анонимная функция Python

Анонимные функции не имеют имени.Мы можем определить анонимную функцию в Python, используя ключевое слово lambda.