Функции Python 3: значение, аргументы, вызов, переменные, списки — Python 3 | Data Science | Нейронные сети | AI
Содержание страницы
Функции – главный и самый важный способ организации и повторного использования кода в Python. Функций не может быть слишком много. 1 функция — это блок кода, который используется для выполнения одного связанного действия.
В Python есть два инструмента для создания функций: def и lambda.
Сначала рассмотрим def. К lambda функции вернемся позже.
Python предоставляет вам множество встроенных функций, таких как print() и т.д., но вы также можете создавать свои собственные функции. Эти функции называются пользовательскими функциями.
Объявление функции начинается ключевым словом def, а результат возвращается в предложении return:
def my_function(x, y, z=1.5):
if z > 1:
return z * (x + y)
else:
return z / (x + y)Ничто не мешает иметь в функции несколько предложений return. Если при выполнении достигнут конец функции, а предложение return не встретилось, то возвращается None.
У функции могут быть позиционные и именованные аргументы.
Именованные аргументы обычно используются для задания значений по умолчанию и необязательных аргументов.
В примере выше:
- x и y – позиционные аргументы,
- а z – именованный.
Следующие вызовы функции эквивалентны:
my_function(5, 6, z=0.7) my_function(3.14, 7, 3.5)
Основное ограничение состоит в том, именованные аргументы должны находиться после всех позиционных (если таковые имеются).
Сами же именованные аргументы можно задавать в любом порядке, это освобождает программиста от необходимости помнить, в каком порядке были указаны аргументы функции в объявлении, важно лишь, как они называются.
Вы можете задать функции для реализации нужной вам функциональности. Вот простые правила для определения функции в Python.
- Ключевое слово для определения функции: def, за которым следуют имя функции и круглые скобки () с параметрами.
- Любые входные параметры или аргументы должны быть помещены в эти круглые скобки.
- В качестве первой команды может быть необязательная конструкция — строка документации функции (эта часть функции — пояснение зачем функция создана, очень рекомендуется использовать для облегчения понимания кода при работе в команде или при повторном возвращении к коду через длительный промежуток времени).
- Блок кода в каждой функции начинается с двоеточия
: и имеет отступ. - Оператор return [выражение] возвращает результат из функции. Оператор return без аргументов аналогичен return None. Функции всегда возвращают значение, хотя бы None.
def function_name( parameter1, parameter2 ): """function_docstring: функция Python предназначена для расчета ROI""" function_suite (algorithms, expressions) return [expression]
По умолчанию, параметры parameter1, parameter2 имеют позиционное поведение, и вам необходимо сообщить их в том же порядке, в котором они были определены.
Пример реализации функцииСледующая функция принимает строку в качестве входного параметра и печатает ее на стандартном экране.
def print_me( str ): """Выводит на печать переданную строку в эту функцию""" print(str) returnВызов функции
В определении функции указывается имя функции, её параметры, а также тело функции (реализация требуемой функциональности).
Как только базовая структура функции создана, вы можете выполнить ее, вызвав ее из другой функции или непосредственно из Python prompt (командной оболочки). Ниже приведен пример вызова функции printme():
# Определение функции
def printme( str ):
"""This prints a passed string into this function"""
print(str)
return
# Теперь вы можете вызвать функцию printme
printme("I'm first call to user defined function!")
printme("Again second call to the same function")Результат:
I'm first call to user defined function! Again second call to the same function
Вы можете вызвать функцию, используя следующие типы аргументов:
- Обязательные аргументы
- Ключевые аргументы
- Аргументы по умолчанию
- Аргументы переменной длины
Позиционные аргументы: указываются простым перечислением
Обязательные аргументы — это аргументы, переданные функции в правильном позиционном порядке. Здесь количество аргументов и их порядок в вызове функции должно точно соответствовать определению функции.
Чтобы вызвать функцию printme() , вам обязательно нужно передать один аргумент, иначе она выдаст следующую синтаксическую ошибку:
# Определение функции def printme( str ): """This prints a passed string into this function""" print(str) return # Теперь вы можете вызвать функцию printme printme()
Ошибка (в функции Printme не указан аргумент):
Traceback (most recent call last):
File "C:/Users/User/Desktop/CodePythonFunc.py", line 8, in <module>
printme()
TypeError: printme() missing 1 required positional argument: 'str'Правильно указать аргумент так:
# Определение функции
def printme( str ):
"""This prints a passed string into this function"""
print(str)
return
# Теперь вы можете вызвать функцию printme
printme("Текстовая переменная")Ключевые аргументыКлючевые аргументы: указываются перечислением
ключ=значение
Когда вы используете ключевые аргументы в вызове функции, вызывающая сторона идентифицирует аргументы по имени параметра.
Плюс в определении функции у ключевого параметра можно указать значение по-умолчанию.
Это позволяет пропускать аргументы или размещать их не по порядку, поскольку интерпретатор Python может использовать предоставленные ключевые слова (ключи) для сопоставления значений с параметрами. Создадим ключевые слова для функции
# Определение функции
def printinfo( name, age ):
"This prints a passed info into this function"
print("Name: " + name + "|Age: " + str(age))
return
# Вызов функции printinfo
printinfo( age=23, name="Anton" )
printinfo( name="Alena", age=20 )Результат:
Name: Anton|Age: 23 Name: Alena|Age: 20
Преимущества ключевых аргументов в функцияхВажно! Позиционные и ключевые аргументы могут быть скомбинированы в одной функции. Позиционные параметры всегда идут перед ключевыми.
- Нет необходимости отслеживать порядок аргументов;
- У ключевых параметров есть значение по умолчанию, которое можно не передавать.
Аргумент по умолчанию — это аргумент, который принимает значение по умолчанию (задается в описании функции), если при вызове функции аргументу не передается значение.
Следующий пример дает представление об аргументах по умолчанию, он печатает возраст по умолчанию, если он не был передан:
# Определение функции
def printinfo( name, age = 35 ):
"This prints a passed info into this function"
print("Name: " + name + "|Age: " + str(age))
return
# Вызов функции printinfo
printinfo( age=19, name="Семен" )
printinfo( name="Николай" )Результат:
Name: Семен|Age: 19 Name: Николай|Age: 35Аргументы переменной длины Передача кортежа в функцию — Использование *args в Python
Вам может потребоваться разработать функцию для большего числа аргументов, чем Вы указали при определении функции.
Эти аргументы называются
В Python форма с одной звездочкой *argsможет использоваться в качестве параметра для отправки в список функций аргументов переменной длины без ключа. Стоит отметить, что звёздочка ( *) является здесь важным элементом, так как слово argsявляется общепринятой традиционной идиомой, хотя оно не поддерживается языком.
Синтаксис для функции с аргументами переменной длины следующий:
def function_name([formal_args,] *args_tuple ):
"""function_docstring"""
function_body
return [expression]Звездочка (*) ставится перед именем переменной, которая содержит в себе кортеж значений без ключевых слов. Кортеж является необязательным параметром.
Рассмотрим простой пример:
# Определение функции
def print_info( parametr_1, *var_tuple ):
"""Эта функция производит вывод переданных аргументов"""
print("Output is: ")
print(parametr_1)
for elem in var_tuple:
print(elem)
return
# Вызов функции printinfo
print_info( 10 )
print_info( 70, 60, 50 )Результат:
Output is: 10 Output is: 70 60 50
Еще один пример использования *args в функции Python:
def multiply(*args):
z = 1
for num in args:
z *= num
print(z)
multiply(4, 5)
multiply(10, 9)
multiply(2, 3, 4)
multiply(3, 5, 10, 6)Результат:
20 90 24 900
Поскольку мы использовали *argsдля отправки списка аргументов переменной длины в нашу функцию, мы смогли передать столько аргументов, сколько пожелали, в вызовы функции.
С помощью *args вы можете создать более гибкий код, который принимает различное количество аргументов без ключевых слов в вашей функции.
**kwargs — сокращение от “keyword arguments”
Форма двойной звездочки **kwargsиспользуется для передачи словарного словаря с аргументами переменной длины в функцию. Опять же, две звездочки ( **) являются здесь важным элементом, так как слово kwargsиспользуется условно, но не поддерживается языком.
Мол *args, **kwargsможет принять столько аргументов, которые вы хотели бы привести к этому. Однако **kwargsотличается от того, *argsчто вам нужно будет назначить ключевые слова.
Во-первых, давайте просто распечатаем **kwargsаргументы, которые мы передаем функции. Мы создадим короткую функцию для этого:
def print_kwargs(**kwargs):
print(kwargs)
print_kwargs(kwargs_1="Shark", kwargs_2=4.5, kwargs_3=True)Давайте запустим программу выше и посмотрим на вывод:
{'kwargs_1': 'Shark', 'kwargs_2': 4.5, 'kwargs_3': True}В зависимости от версии Python 3, которую вы используете в данный момент, тип данных словаря может быть неупорядоченным. В Python 3.6 и выше вы получите пары ключ-значение по порядку, но в более ранних версиях пары будут выводиться в случайном порядке.
Важно отметить, что kwargsсозданный словарь создан, и мы можем работать с ним так же, как мы можем работать с другими словарями.
Давайте создадим еще одну короткую программу, чтобы показать, как мы можем использовать **kwargs. Здесь мы создадим функцию для приветствия словаря имен. Сначала мы начнем со словаря из двух имен:
def print_values(**kwargs):
for key, value in kwargs.
items():
print("The value of {} is {}".format(key, value))
print_values(my_name="Sammy", your_name="Casey")Результат:
The value of my_name is Sammy The value of your_name is Casey
Использование **kwargsдает нам гибкость в использовании ключевых аргументов в нашей программе. Когда мы используем **kwargsв качестве параметра, нам не нужно знать, сколько аргументов мы в конечном итоге хотели бы передать функции.
Мы также можем использовать *argsи **kwargsдля передачи аргументов в функции.
Сначала давайте рассмотрим пример с *args.
def some_args(arg_1, arg_2, arg_3):
print("arg_1:", arg_1)
print("arg_2:", arg_2)
print("arg_3:", arg_3)
args = ("Sammy", "Casey", "Alex")
some_args(*args)В функции выше, существует три параметра , определенный как arg_1, arg_и arg_3. Функция распечатает каждый из этих аргументов. Затем мы создаем переменную, для которой задано итеративное значение (в данном случае кортеж ), и можем передать эту переменную в функцию с синтаксисом звездочки.
Когда мы запустим код, мы получим следующий вывод:
arg_1: Sammy arg_2: Casey arg_3: Alex
Мы также можем изменить вышеприведенную программу для типа данных итеративного списка с другим именем переменной. Давайте также объединим *argsсинтаксис с именованным параметром:
def some_args(arg_1, arg_2, arg_3):
print("arg_1:", arg_1)
print("arg_2:", arg_2)
print("arg_3:", arg_3)
my_list = [2, 3]
some_args(1, *my_list)Если мы запустим программу выше, она выдаст следующий вывод:
arg_1: 1 arg_2: 2 arg_3: 3
Аналогично, **kwargsаргументы с ключевым словом могут использоваться для вызова функции.
Мы установим переменную, равную словарю с 3 парами ключ-значение (мы будем использовать kwargsздесь, но она может называться как угодно), и передадим ее функции с 3 аргументами:
def some_kwargs(kwarg_1, kwarg_2, kwarg_3):
print("kwarg_1:", kwarg_1)
print("kwarg_2:", kwarg_2)
print("kwarg_3:", kwarg_3)
kwargs = {"kwarg_1": "Val", "kwarg_2": "Harper", "kwarg_3": "Remy"}
some_kwargs(**kwargs)Результат:
kwarg_1: Val kwarg_2: Harper kwarg_3: Remy
Мы можем использовать специальный синтаксис *args и **kwargs внутри определения функции, чтобы передать переменное число аргументов функции.
Создание функций, которые принимают *argsи **kwargsлучше всего использовать в ситуациях, когда вы ожидаете, что количество входов в списке аргументов останется относительно небольшим. Использование *argsи **kwargsв первую очередь для обеспечения удобочитаемости и удобства, но следует делать с осторожностью.
При комбинировании параметров параметр с двумя звездочками записывается самым последним. Если в определении функции указывается комбинация параметров с одной звездочкой и двумя звездочками, то функция примет любые переданные ей параметры:
def func(*args, **kwargs):
"""Функция примет любые параметры и выведет их на печать"""
for elem in args: # Перебираем список с переданными параметрами
print(elem, end=" ")
for key, value in kwargs.items(): # Перебираем словарь с переданными параметрами
print("{0} => {1}".format(key, value), end=" ")
print("") #Перенос строки
func(35, 10, a=1, b=2, c=3) # Выведет: 35 10 a => 1 c => 3 b => 2
func(10) # Выведет: 10
func(3, a=1, b=2) # Выведет: a => 1 b => 2Результат:
35 10 a => 1 b => 2 c => 3 10 3 a => 1 b => 2
Еще один пример использования комбинации *args & **kwargs:
# Описываем функцию
def echo(*args, **kwargs):
print(args, kwargs)
# Задаем список
args = (10, 20)
# Задаем словарь
kwargs = {'a':30, 'b':40}
#Вызываем функцию
echo(*args, **kwargs)Результат:
(10, 20) {'a': 30, 'b': 40}Порядок аргументов в функции PythonАргументы внутри вызова функции должны стоять в определенном порядке:
- Позиционные аргументы (arg_1, arg_2)
- *args
- Ключевые аргументы (kw_1=»значение 1″, kw_2=»значение 2″)
- **kwargs
Рассмотрим детальный пример с использованием всех функций:
def function_print(arg_1, arg_2, *args, kw_1="значение 1", kw_2="значение 2", **kwargs):
""" Обобщенный пример вывода всех аргументов, переданных функции """
print("===Позиционные аргументы===")
print(str(arg_1) + "," + str(arg_2))
print("===*args===")
for elem in args: # Перебираем список с переданными параметрами
print(elem, end=" ")
print("") #Перенос строки
print("===Ключевые аргументы===")
print(kw_1 + "," + kw_2)
print("===**kwargs===")
for key, value in kwargs.
items(): # Перебираем словарь с переданными параметрами
print("{0} => {1}".format(key, value), end=" ")
print("") #Перенос строки
function_print(1, 2, 4, kw_2="Иван", var1=4)Результат:
===Позиционные аргументы=== 1,2 ===*args=== 4 ===Ключевые аргументы=== значение 1,Иван ===**kwargs=== var1 => 4Передача списка (list) по ссылке в функцию
def myfunction (list):
list.append(40)
print ("Modified list inside a function: ", list)
return
mylist=[10,20,30]
myfunction(mylist)
print(mylist)Следующий результат подтверждает, что аргументы передаются по ссылке в функцию Python:
Modified list inside a function: [10, 20, 30, 40] [10, 20, 30, 40]Область действия переменной — область видимости переменных
Переменные в программе могут быть доступны или недоступны в разных местах этой программы. Это зависит от того, где вы объявили переменную.
Область действия переменной определяет ту часть программы, в которой вы можете получить доступ к определенному идентификатору (т.е. к значению переменной и возможности эту переменную поменять). В Python есть две основные области видимости переменных:
- Глобальные переменные
- Локальные переменные
Область видимости переменной в Python называют также пространством имен. Любая переменная, которой присвоено значение внутри функции, по умолчанию попадает в локальное пространство имен.
Локальное пространство имен создается при вызове функции, и в него сразу же заносятся аргументы функции. По завершении функции локальное пространство имен уничтожается (хотя бывают и исключения, см. ниже раздел о замыканиях).
Переменные внутри функции – локальные. Поиск переменных: сперва среди локальных, потом среди глобальных.
Рассмотрим следующую функцию:
def func():
a = []
for i in range(5):
a.append(i)При вызове func() создается пустой список a, в него добавляется 5 элементов, а затем, когда функция завершается, список a уничтожается. Но допустим, что мы объявили a следующим образом:
a = []
def func():
for i in range(5):
a.append(i)Присваивать значение глобальной переменной внутри функции допустимо, но такие переменные должны быть объявлены глобальными с помощью ключевого слова global:
a = None
def bind_a_variable():
global a
a = []
bind_a_variable()
print aРезультат:
[]
Функции можно объявлять в любом месте, в том числе допустимы локальные функции, которые динамически создаются внутри другой функции при ее вызове:
def outer_function(x, y, z):
def inner_function(a, b, c):
pass
passЗдесь функция inner_function не существует, пока не вызвана функция outer_function. Как только outer_function завершает выполнение, inner_function уничтожается.
Не рекомендуется злоупотреблять ключевым словом global. Обычно глобальные переменные служат для хранения состояния системы. Если вы понимаете, что пользуетесь ими слишком часто, то стоит подумать о переходе к объектно-ориентированному программированию (использовать классы).
Вложенные функции могут обращаться к локальному пространству имен объемлющей функции, но не могут связывать в нем переменные. Подробнее смотрите в разделе о замыканиях.
Строго говоря, любая функция локальна в какой-то области видимости, хотя это может быть и область видимости на уровне модуля.
Возврат нескольких значений из одной функцииВ Python существует возможность возвращать из функции несколько значений.
Вот простой пример:
def f():
a = 5
b = 6
c = 7
return a, b, c
var1, var2, var3 = f()
print("var1={0} var2={1} var3={2}".format(var1, var2, var3))Результат:
var1=5 var2=6 var3=7
В анализе данных и других научных приложениях это встречается сплошь и рядом, потому что многие функции вычисляют несколько результатов.
На самом деле, функция возвращает только один объект, кортеж, который затем распаковывается в результирующие переменные.
Этот пример можно было бы записать и так:
def f():
a = 5
b = 6
c = 7
return a, b, c
return_value = f()
print(return_value)Результат:
(5, 6, 7)
В таком случае return_value было бы кортежем, содержащим все три возвращенные переменные.
Иногда разумнее возвращать несколько значений не в виде кортежа, а в виде словаря:
def f():
a = 5
b = 6
c = 7
return {'a' : a, 'b' : b, 'c' : c}
return_value = f()
print(return_value)Результат:
{'a': 5, 'b': 6, 'c': 7}Функции являются объектамиПоскольку функции в Python – объекты, становятся возможны многие конструкции, которые в других языках выразить трудно. Пусть, например, мы производим очистку данных и должны применить ряд преобразований к следующему списку строк:
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']
Всякий, кому доводилось работать с присланными пользователями данными опроса, ожидает такого рода мусора. Чтобы сделать такой список строк пригодным для анализа, нужно произвести различные операции: удалить лишние пробелы и знаки препинания, оставить заглавные буквы только в нужных местах.
Первая попытка могла бы выглядеть так:
import re # Модуль регулярных выражений
def clean_strings(strings):
result = []
for value in strings:
value = value.strip()
value = re.sub('[!#?]', '', value) # удалить знаки препинания
value = value.title()
result.append(value)
return result
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']
states = clean_strings(states)
print(states)Результат:
['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']
Другой подход, который иногда бывает полезен, – составить список операций, которые необходимо применить к набору строк:
import re # Модуль регулярных выражений
def remove_punctuation(value):
return re.sub('[!#?]', '', value)
def clean_strings(strings, ops):
result = []
for value in strings:
for function in ops:
value = function(value)
result.append(value)
return result
clean_ops = [str.strip, remove_punctuation, str.title]
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']
states = clean_strings(states, clean_ops)
print(states)Результат:
['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']
Подобный функциональный подход позволяет задать способ модификации строк на очень высоком уровне. Степень повторной используемости функции clean_strings определенно возросла!
Функции можно передавать в качестве аргументов другим функциям, например встроенной функции map, которая применяет переданную функцию к коллекции:
import re # Модуль регулярных выражений
def remove_punctuation(value):
return re.
sub('[!#?]', '', value)
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']
states = list(map(remove_punctuation, states))
states = list(map(str.strip, states))
states = list(map(str.title, states))
print(states)Результат:
['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']Анонимные функции или Лямбда-функции. Lambda Function Python 3
- Лямбда-функции позволяют создавать «встроенные» функции, которые полезны для функционального программирования. Например, с Map, Filter или Reduce.
- Lambda — это инструмент для создания обработчиков обратного вызова (callback handlers).
Lambda Function — это анонимная однострочная функция, которая возвращает всегда 1 результат.
Определяются они с помощью ключевого слова lambda
ИмяФункции = lambda переменная_1, переменная_2, …, переменная_N: <выражение, которое использует переменные>
Например, функция equiv_anon:
equiv_anon = lambda x: x * 2
эквивалентна функции short_function:
def short_function(x):
return x * 2Лямбда-функции особенно удобны в ходе анализа данных, потому что, как вы увидите, во многих случаях функции преобразования данных принимают другие функции в качестве аргументов. Часто быстрее (и чище) передать лямбда-функцию, чем писать полноценное объявление функции или даже присваивать лямбда-функцию локальной переменной.
Рассмотрим такой простенький пример:
def apply_to_list(some_list, f):
return [f(x) for x in some_list]
ints = [4, 0, 1, 5, 6]
result = apply_to_list(ints, lambda x: x * 2)
print(result)Результат:
[8, 0, 2, 10, 12]
Можно было бы, конечно, написать [x * 2 for x in ints], но в данном случае нам удалось передать функции apply_to_list пользовательский оператор.
Еще пример: пусть требуется отсортировать коллекцию строк по количеству различных букв в строке.
Для этого можно передать лямбда-функцию методу списка sort:
strings = ['foo', 'card', 'bar', 'aaaa', 'abab'] strings.sort(key=lambda x: len(set(list(x)))) print(strings)
Результат:
['aaaa', 'foo', 'abab', 'bar', 'card']Для чего хороша лямбда-функция? Зачем нужная lambda function в Python?
Ответ:
- Нам не нужна лямбда функция, мы могли бы обойтись без нее. Но…
- Есть определенные ситуации, когда это удобно — lambda делает написание кода немного легче, а написанный код — немного чище.
Какие ситуации?
Что ж, ситуации, в которых нам нужна простая одноразовая функция: функция, которая будет использоваться только один раз.
Обычно функции создаются для одной из двух целей: (а) для уменьшения дублирования кода или (б) для модульности кода.
- Если ваше приложение содержит повторяющиеся фрагменты кода в разных местах, то вы можете поместить одну копию этого кода в функцию, дать имя функции, а затем — используя это имя функции — вызвать ее из разных мест в вашем коде.
- Если у вас есть кусок кода, который выполняет одну четко определенную операцию — но он действительно длинный и грубый и прерывает читаемый поток вашей программы, то вы можете извлечь этот длинный грубый код и поместить его в самостоятельную функцию.
Но предположим, что вам нужно создать функцию, которая будет использоваться только один раз — вызываться только из одного места в вашем приложении. Ну, во-первых, вам не нужно давать имя функции. Это может быть «анонимно». И вы можете просто определить его прямо там, где вы хотите его использовать. Вот где лямбда полезна.
Еще один пример использования lambda функции:
colors = ["Goldenrod", "Purple", "Salmon", "Turquoise", "Cyan"] normalized_colors = list(map(lambda s: s.casefold(), colors)) print(normalized_colors)
Результат:
['goldenrod', 'purple', 'salmon', 'turquoise', 'cyan']Неправильное использование: именование лямбда-выражений
PEP8, официальное руководство по стилю Python, советует никогда не писать такой код:
normalize_case = lambda s: s.casefold()
Вышеприведенный оператор создает анонимную функцию, а затем назначает ее переменной. Приведенный выше код игнорирует причину полезности лямбда-функций: лямбда-функции могут передаваться без необходимости вначале присваивать их переменной.
Если вы хотите создать однострочную функцию и сохранить ее в переменной, вы должны использовать defвместо этого:
def normalize_case(s): return s.casefold()
PEP8 рекомендует это, потому что именованные функции — это обычная и понятная вещь. Именованная функция также имеет следующее преимущество: назначая нашей функции правильное имя, Вы упрощаете отладку кода. В отличие от функций, определенных с помощью def, лямбда-функции никогда не имеют имени (это всегда <lambda>).
Если вы хотите создать функцию и сохранить ее в переменной, определите свою функцию с помощью def . Это именно то, для чего существует именованная функция. Не имеет значения, является ли ваша функция одной строкой кода или вы определяете функцию внутри другой функции, def отлично работает для этих случаев использования.
Периодически встречаются лямбда-выражения, которые используются для подстановки в функции python, которые уже содержат функциональность для решения задачи. Нужно только посмотреть в документацию по функциям.
Например, возьмем этот код:
sorted_numbers = sorted(numbers, key=lambda n: abs(n))
Программист, написавший этот код, вероятно, узнал, что лямбда-выражения используются для создания функции, которую можно передавать в другую функцию.
Поскольку abs (которая возвращает абсолютное значение числа) является функцией и все функции могут быть переданы, мы могли бы написать приведенный выше код следующим образом:
sorted_numbers = sorted(numbers, key=abs)
Теперь этот пример может показаться надуманным, но нередко чрезмерно используют лямбда-выражения таким образом. Вот еще один пример, который иногда встречается:
pairs = [(4, 11), (8, 8), (5, 7), (11, 3)] sorted_by_smallest = sorted(pairs, key=lambda items: min(items))
Поскольку мы принимаем те же аргументы, что и при передаче min, нам не нужен этот дополнительный вызов функции. Вместо этого мы можем просто передать min функцию key:
pairs = [(4, 11), (8, 8), (5, 7), (11, 3)] sorted_by_smallest = sorted(pairs, key=min)
Вам не нужна лямбда-функция, если у вас уже есть другая функция, которая делает то, что вы хотите.
Замыкания: функции, возвращающие функции. Динамически сгенерированная функцияЗамыканием называется любая динамически сгенерированная функция, возвращенная из другой функции. Основное свойство замыкания состоит в том, что оно имеет доступ к переменным, определенным в том локальном пространстве имен, в котором было создано.
Вот очень простой пример:
def make_closure(a):
def closure():
print('Я знаю секрет: %d' % a)
return closure
closure = make_closure(5)
closure()Результат:
Я знаю секрет: 5
Разница между замыканием и обычной функцией Python состоит в том, что замыкание сохраняет доступ к пространству имен (функции), в котором было создано, даже если создавшая ее функция уже завершилась.
Так, в примере выше, возвращенное замыкание печатает строку «Я знаю секрет: 5», в какой бы момент ее ни вызвать.
Хотя обычно создают замыкания со статическим внутренним состоянием (в данном случае только значение a), ничто не мешает включить в состав состояния изменяемый объект – словарь, множество, список – и затем модифицировать его.
Например, ниже приведена функция, которая возвращает функцию, запоминающую, с какими аргументами вызывалась объемлющая функция:
def make_watcher():
have_seen = {}
def has_been_seen(x):
if x in have_seen:
return True
else:
have_seen[x] = True
return False
return has_been_seen
watcher = make_watcher()
vals = [5, 6, 1, 5, 1, 6, 3, 5]
print([watcher(x) for x in vals])Результат:
[False, False, False, True, True, True, False, True]
Однако следует иметь в виду одно техническое ограничение: изменять внутреннее состояние объектов (например, добавлять в словарь пары ключ-значение) можно, но связывать переменные в области видимости объемлющей функции – нельзя. Обойти это ограничение можно, например, путем модификации словаря или списка вместо присваивания значений переменным.
def make_counter():
count = [0]
def counter():
# увеличить счетчик и вернуть новое значение
count[0] += 1
return count[0]
return counter
counter = make_counter()
print(counter())Результат:
1
Возникает вопрос, зачем все это нужно? На практике можно написать очень общую функцию с кучей параметров, а затем изготовить на ее основе более простые специализированные функции.
Вот пример функции форматирования строк:
def format_and_pad(template, space):
def formatter(x):
return (template % x).rjust(space)
return formatter
fmt = format_and_pad('%.4f', 15)
print(fmt(1.756))Результат:
' 1.Каррирование: частичное фиксирование аргументов функции7560'
В информатике термином каррирование обозначается процедура порождения новых функций из существующих путем фиксирования некоторых аргументов.
Пусть, например, имеется тривиальная функция сложения двух чисел:
def add_numbers(x, y):
return x + yМы можем породить на ее основе новую функцию одной переменной, add_five, которая прибавляет к своему аргументу 5:
add_five = lambda y: add_numbers(5, y)
Говорят, что второй аргумент функции add_numbers каррирован. Ничего особо примечательного здесь нет, поскольку мы просто определили новую функцию, которая вызывает существующую.
Теперь запустим полный код:
def add_numbers(x, y):
return x + y
add_five = lambda y: add_numbers(5, y)
print(add_five(10))Результат:
15
Стандартный модуль functools упрощает эту процедуру за счет функции partial:
from functools import partial
def add_numbers(x, y):
return x + y
add_five = partial(add_numbers, 5)
print(add_five(10))Также вернет 15
При обсуждении библиотеки pandas мы будем пользоваться этой техникой для создания специализированных функций преобразования временных рядов:
# вычислить скользящее среднее временного ряда x за 60 дней ma60 = lambda x: pandas.rolling_mean(x, 60) # вычислить скользящие средние за 60 дней всех временных рядов в data data.apply(ma60)
5 1 голос
Рейтинг статьи
Навигация по записям
Python functions — Введение в Python
Введение в Pythonyoutube.com/embed/3rI4H7HhrS0″/>
Видео может быть заблокировано из-за расширений браузера. В статье вы найдете решение этой проблемы.Functions in Python
Common structure
def name(arg1, ... argN, kwarg1, ... kwargN, *args, **kwargs):
"""Short description
Long description...
"""
....operator1
....operatorN
....return value
Naming rules
- lower_underscore_case
- letters, numbers, underscores
- leading underscore for private funcs
myFunc() # bad
My_func # bad
100_top_users # SyntaxError
get_top_users() # ok
_fetch_data() # perhaps you shouldn't call that directly
Why need docstring
- Keep code and docs at the same place
- Useful for IDEs, editors
- Build documentation from code
- help(func) or func.doc prints the doc
- Doctests
def test(a, b, c=3, d=4)
test() TypeError
test(1, 2) a=1, b=2, c=3, d=4
test(4, 10, c=22) a=4, b=10, c=22, d=4
test(a=6, b=5) a=6, b=5, c=3, d=4
test(d=4, a=1) TypeError
test(c=1, b=4, a=5) a=5, b=4, c=1, d=4
test(9, 8, 7) a=9, b=8, c=7, d=4
test(9, 8, 7, 6) a=9, b=8, c=7, d=6
What does function return
- Use return <value> statement
- There might be several return statements
- None if there is no return statement
- Use tuple to return multiple values
def func(foo, bar, baz=42):
if baz == 42:
return (False, "some-error")
else:
return (True, "OK")
ok, message = func(1, 2) False, "some-error"
ok, message = func(1, 2, baz=3) True, "OK"
Function is object
- Copy, delete function
- Pass it into a function call
- Might be either a key or value of a dict
- Has special attrs (__name__, __module__, etc)
def user_auth(user):
pass
def user_logout(user)
pass
actions = {"auth": user_auth, "logout": user_logout}
actions[action](user)
args, kwargs
- def (*args, **kwargs)
- aggregate rest arguments and keyword arguments
- args is a tuple, kwargs is a dict
- useful when you construct arguments step by step
params = {"name": "Ivan", "age": 29}
if .
..
params["created_at"] = datetime.now()
if ...
params["friends"] = ["Joe", "Mary"]
create_user(**params)
create_user(name="Ivan", age=29,
created_at=datetime.now(),
friends=["Joe", "Mary"]
)
Never use mutable defaults!
def test(a=[]):
a.append(1)
print a
test() [1]
test() [1, 1]
test() [1, 1, 1]
test() [1, 1, 1, 1]
Lambdas
- Came from functional programming
- A short expression with only one statement inside
- Pros:
- just-in-place function
- supports argument unpacking
- long ones are ugly
- hard to debug
double = lambda x: 2 * x
map(double, [1, 2, 3]) [2, 4, 6]
calc = lambda (x, y, x): x * y - z
map(calc, [(1, 2, 3), (4, 5, 6)]) [-1, 14]
avg = lambda *args: sum(args) / float(len(args))
avg(1, 2, 3) 2.0
lambda x: pass SyntaxError
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты.
Ошибки, сложный материал, вопросы >Нашли опечатку или неточность?
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Что-то не получается или материал кажется сложным?
Загляните в раздел «Обсуждение»:
- задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
- расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
- изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете
Как использовать в 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.
Синтаксис
Как вы видели в предыдущих разделах, лямбда имеет синтаксические отличия от нормальной функции. В частности, лямбда имеет следующие характеристики:
- Она может содержать только выражения и не может включать операторы в свое тело.
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 syntaxIIFE
Вы уже видели несколько примеров немедленного запуска функции:
>>> (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()andnsmallest(): в модуле алгоритма очереди кучи 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
Была ли вам полезна эта статья?
[18 / 4.8]def. Python 3 для начинающих — RUUD
Содержание статьи:В языках программирования функции являются именованной частью кода. Это отдельные блоки в тексте программы.
Определяются с помощью зарезервированного слова def. В Python к функциям можно обращаться неограниченное количество раз из любой части сценария.Зачем нужны функции
Функции – это незаменимый инструмент программиста. С их помощью разработчик структурирует программу, делая ее понятней и компактнее. С помощью функций можно добиться многократного использования отдельной части кода без его повторного написания.
Вам будет интересно:Конструкция выбора switch case PHP
Это простейший способ упаковать логику выполнения отдельных частей программы. При этом сокращается объем и время, которое специалист тратит на создание сценария.
Как написать первую функцию
В Python 3 для начинающих свое знакомство с программированием есть самая простая функция print(). Чтобы увидеть ее в действии вам понадобится среда разработки. Для этого скачайте дистрибутив языка с официального сайта и установите Python на компьютер.
Откройте меню «Пуск» и в списке программ найдите Python 3. Разверните его щелчком левой клавиши. В открывшемся списке найдите среду IDLE и запустите ее. Наберите print(«Hello, World!») и нажмите «Ввод». Интерпретатор вернет результат вашей первой функции.
Некоторые программисты предпочитают работать в консоли. Если вы относитесь к их числу, нажмите win+R и введите команду python.exe. Откроется обычный интерпретатор, только с интерфейсом cmd. Наберите программу описанным выше образом и нажмите Enter в конце, чтобы увидеть результат.
Как использовать def
Вам будет интересно:Объектно-ориентированное программирование на Python: классы, описание и особенности
Новые функции создаются с помощью инструкции def. Они так же эффективны, как и встроенные print() или open(), но отличаются от функций в компилирующих языках. Python def относится к исполняемым инструкциям. Это означает, что функции не существует, пока интерпретатор ее не увидит, и не перейдет к ее исполнению.
Инструкция def создает новый объект и дает ему название.
То есть когда интерпретатор приступает к реализации, он создает новый объект и связывает его с именем, указанным после def. Чтобы хранить данные к функциям можно прикреплять различные атрибуты.Теперь давайте напишем функцию, возвращающую фразу «Hello, World!», только с использованием def:
- >>> def здравствуй_мир():
- print(«Hello, World!»)
- >>> здравствуй_мир() #вызов функции
- Hello, World!
Синтаксис функций и return
Инструкция def в Python состоит из заголовка и пишется по следующим правилам:
- >>>def (аргумент 1, аргумент 2, аргумент N):
После заголовка следует блок инструкций, который начинается с обязательного отступа. В IDLE интерпретатор сделает его автоматически. Но в блокноте или другом текстовом редакторе вы можете забыть нажать Tab. Тогда функция не запустится. Программный код в блоке инструкции называется телом функции и выполняется каждый раз при ее вызове.
Также в теле иногда находится return:
- def (аргумент 1, аргумент 2, аргумент N):
- …
- return
Return завершает работу функции и передает вызывающей программе объект-результат. Инструкция не является обязательной. Функция будет работать без return, и завершится, когда поток управления достигнет конца ее тела.
Параметры и аргументы
Каждой функции можно передавать параметры, которые указываются в скобках после def. В Python они записываются как переменные, разделенные запятыми. Значения или ссылки на объекты этим именам присваиваются в блоке за двоеточием. После операции присвоения их принято называть аргументами, а не параметрами.
Аргументы внутри функции никак не связаны с объектами вне ее, поэтому в программировании их относят к локальным переменным. Область видимости ограничена блоком функции, который начинается с def и заканчивается return. Чтобы было понятнее, приведем пример:
- x = 12 #присваиваем переменным ссылки на целочисленные объекты
- y = 34
- >>>def example(x,y): #создаем функцию с именем example
- x = «Hello» #присваиваем значения аргументам x, y
- y = «Python»
- print(x, y, sep= «, »)
- return None
- >>>example(x, y) #вызываем функцию, не забыв указать параметры
- Hello, Python
- >>>print(x, y)
- 12 34
Обратите внимание на предпоследнюю строчку кода.
В интерпретаторе Python команда print() вернула переменные x и y из глобальной области видимости.Значения аргументов не обязательно указывать внутри функции, можно их вписать вручную при ее вызове:
- >>>def E_2(x, y):
- return x + y
- >>>E_2(«Hello, » «Python!») #чтобы слова были разделены, поставьте пробел перед закрывающей кавычкой
- Hello, Python!
- E_2(5, 4)
- 10
Как видно из примера с простой функцией E_2, результат полностью зависит от типа объектов x и y. В первом случае E_2 выполнила конкатенацию, а во втором — арифметическую операцию сложения. В этом заключается принцип полиморфизма и динамической типизации. То, что объекты определяют синтаксический смысл, обуславливает гибкость и простоту языка. Не нужно тратить время на то, чтобы отдельно указать тип данных, с которым работает функция.
Правило LEGB
Это правило касается работы с переменными в разных областях видимости. По умолчанию все имена, которые вы создаете в теле функции, считаются локальными. А имена в модуле являются глобальными. При желании именам можно присвоить значение переменных верхнего уровня с помощью инструкции notlocal и global.
Правило LEGB объясняет схему разрешения имен:
- Как только интерпретатор находит переменную внутри инструкции def, он сначала выполняет поиск значений в локальной области видимости.
- Если поиск не дает результата, он переходит к области видимости любой всеобъемлющей инструкции def.
- Дальше интерпретатор двигается к глобальным именам в верхнем уровне модуля и тем, что обозначены как global.
- Если поиск не дает результатов, интерпретатор ищет имена во встроенной области видимости языка Python.
- >>>L = 85
- >>>R = 23
- >>>def пример_2(K):
- R = 10
- C = L + K+R
- return C
- >>>пример_2(5)
- 100
- >>>f = lambda x, y, z: x + y + z
- >>>f(2, 3, 4)
- 9
- >>> def здравствуй_мир():
- print(«Hello, World!»)
- >>> здравствуй_мир() #вызов функции
- Hello, World!
- >>>def <имя>(аргумент 1, аргумент 2, аргумент N):
- def <имя>(аргумент 1, аргумент 2, аргумент N):
- …
- return <значение>
- x = 12 #присваиваем переменным ссылки на целочисленные объекты
- y = 34
- >>>def example(x,y): #создаем функцию с именем example
- x = «Hello» #присваиваем значения аргументам x, y
- y = «Python»
- print(x, y, sep= «, »)
- return None
- >>>example(x, y) #вызываем функцию, не забыв указать параметры
- Hello, Python
- >>>print(x, y)
- 12 34
- >>>def E_2(x, y):
- return x + y
- >>>E_2(«Hello, » «Python!») #чтобы слова были разделены, поставьте пробел перед закрывающей кавычкой
- Hello, Python!
- E_2(5, 4)
- 10
- Как только интерпретатор находит переменную внутри инструкции def, он сначала выполняет поиск значений в локальной области видимости.
- Если поиск не дает результата, он переходит к области видимости любой всеобъемлющей инструкции def.
- Дальше интерпретатор двигается к глобальным именам в верхнем уровне модуля и тем, что обозначены как global.
- Если поиск не дает результатов, интерпретатор ищет имена во встроенной области видимости языка Python.
- >>>L = 85
- >>>R = 23
- >>>def пример_2(K):
- R = 10
- C = L + K+R
- return C
- >>>пример_2(5)
- 100
- >>>f = lambda x, y, z: x + y + z
- >>>f(2, 3, 4)
- 9
Рассмотрим наглядный пример:
Переменные L и R находятся на верхнем уровне и являются глобальными именами.
R, C и K – это локальные переменные, так как присваивание значения происходит внутри инструкции def.
Интерпретатор сначала выполняет операцию сложения для локальных R, C и K, игнорируя переменную R вне инструкции def. Потом ищет L, и не найдя ее среди имен local, переходит на верхний уровень.
Что такое lambda
Помимо def, в Python функции можно создавать с помощью специальных выражений, одно из которых — lambda. Свое оригинальное название получила в честь лямбда-исчислений языка LISP.
Как и def, lambda создает функцию, которую можно будет в дальнейшем вызвать, но не связывает ее с каким-нибудь именем. На практике lambda используют, когда нужно отложить выполнение фрагмента кода.
Основы лямбда-выражений
По внешнему виду lambda-выражения напоминают инструкции def. Вначале пишется ключевое слово lambda, потом аргументы, двоеточие и само выражение:
Тело лямбда представляет собой одно единственное выражение, а не блок инструкций. За счет этого lambda ограничена в возможностях и не настолько универсальна как def. В ней может быть реализована только логика, без циклов while или for.
Для лямбда действуют аналогичные с def правила поиска переменных. Имена, указанные вне выражения, являются глобальными, внутри – локальными, и они никак не влияют друг на друга.
Lambda-выражения очень удобно встраивать в программу. За счет небольшого размера они минимизируют и упрощают код. Но использование лямбда не является принципиальным. В Python 3 начинающим для работы будет достаточно инструкции def.
Источник
def. Python 3 для начинающих
В языках программирования функции являются именованной частью кода. Это отдельные блоки в тексте программы. Определяются с помощью зарезервированного слова def. В Python к функциям можно обращаться неограниченное количество раз из любой части сценария.
Зачем нужны функции
Функции – это незаменимый инструмент программиста. С их помощью разработчик структурирует программу, делая ее понятней и компактнее. С помощью функций можно добиться многократного использования отдельной части кода без его повторного написания.
Это простейший способ упаковать логику выполнения отдельных частей программы. При этом сокращается объем и время, которое специалист тратит на создание сценария.
Как написать первую функцию
В Python 3 для начинающих свое знакомство с программированием есть самая простая функция print(). Чтобы увидеть ее в действии вам понадобится среда разработки. Для этого скачайте дистрибутив языка с официального сайта и установите Python на компьютер.
Откройте меню «Пуск» и в списке программ найдите Python 3. Разверните его щелчком левой клавиши. В открывшемся списке найдите среду IDLE и запустите ее. Наберите print(«Hello, World!») и нажмите «Ввод». Интерпретатор вернет результат вашей первой функции.
Некоторые программисты предпочитают работать в консоли. Если вы относитесь к их числу, нажмите win+R и введите команду python.exe. Откроется обычный интерпретатор, только с интерфейсом cmd. Наберите программу описанным выше образом и нажмите Enter в конце, чтобы увидеть результат.
Как использовать def
Новые функции создаются с помощью инструкции def. Они так же эффективны, как и встроенные print() или open(), но отличаются от функций в компилирующих языках. Python def относится к исполняемым инструкциям. Это означает, что функции не существует, пока интерпретатор ее не увидит, и не перейдет к ее исполнению.
Инструкция def создает новый объект и дает ему название. То есть когда интерпретатор приступает к реализации, он создает новый объект и связывает его с именем, указанным после def. Чтобы хранить данные к функциям можно прикреплять различные атрибуты.
Теперь давайте напишем функцию, возвращающую фразу «Hello, World!», только с использованием def:
Синтаксис функций и return
Инструкция def в Python состоит из заголовка и пишется по следующим правилам:
После заголовка следует блок инструкций, который начинается с обязательного отступа. В IDLE интерпретатор сделает его автоматически. Но в блокноте или другом текстовом редакторе вы можете забыть нажать Tab. Тогда функция не запустится. Программный код в блоке инструкции называется телом функции и выполняется каждый раз при ее вызове.
Также в теле иногда находится return:
Return завершает работу функции и передает вызывающей программе объект-результат. Инструкция не является обязательной. Функция будет работать без return, и завершится, когда поток управления достигнет конца ее тела.
Параметры и аргументы
Каждой функции можно передавать параметры, которые указываются в скобках после def. В Python они записываются как переменные, разделенные запятыми. Значения или ссылки на объекты этим именам присваиваются в блоке за двоеточием. После операции присвоения их принято называть аргументами, а не параметрами.
Аргументы внутри функции никак не связаны с объектами вне ее, поэтому в программировании их относят к локальным переменным. Область видимости ограничена блоком функции, который начинается с def и заканчивается return. Чтобы было понятнее, приведем пример:
Обратите внимание на предпоследнюю строчку кода. В интерпретаторе Python команда print() вернула переменные x и y из глобальной области видимости.
Значения аргументов не обязательно указывать внутри функции, можно их вписать вручную при ее вызове:
Как видно из примера с простой функцией E_2, результат полностью зависит от типа объектов x и y. В первом случае E_2 выполнила конкатенацию, а во втором — арифметическую операцию сложения. В этом заключается принцип полиморфизма и динамической типизации. То, что объекты определяют синтаксический смысл, обуславливает гибкость и простоту языка. Не нужно тратить время на то, чтобы отдельно указать тип данных, с которым работает функция.
Правило LEGB
Это правило касается работы с переменными в разных областях видимости. По умолчанию все имена, которые вы создаете в теле функции, считаются локальными. А имена в модуле являются глобальными. При желании именам можно присвоить значение переменных верхнего уровня с помощью инструкции notlocal и global.
Правило LEGB объясняет схему разрешения имен:
Рассмотрим наглядный пример:
Переменные L и R находятся на верхнем уровне и являются глобальными именами. R, C и K – это локальные переменные, так как присваивание значения происходит внутри инструкции def.
Интерпретатор сначала выполняет операцию сложения для локальных R, C и K, игнорируя переменную R вне инструкции def. Потом ищет L, и не найдя ее среди имен local, переходит на верхний уровень.
Что такое lambda
Помимо def, в Python функции можно создавать с помощью специальных выражений, одно из которых — lambda. Свое оригинальное название получила в честь лямбда-исчислений языка LISP.
Как и def, lambda создает функцию, которую можно будет в дальнейшем вызвать, но не связывает ее с каким-нибудь именем. На практике lambda используют, когда нужно отложить выполнение фрагмента кода.
Основы лямбда-выражений
По внешнему виду lambda-выражения напоминают инструкции def. Вначале пишется ключевое слово lambda, потом аргументы, двоеточие и само выражение:
Тело лямбда представляет собой одно единственное выражение, а не блок инструкций. За счет этого lambda ограничена в возможностях и не настолько универсальна как def. В ней может быть реализована только логика, без циклов while или for.
Для лямбда действуют аналогичные с def правила поиска переменных. Имена, указанные вне выражения, являются глобальными, внутри – локальными, и они никак не влияют друг на друга.
Lambda-выражения очень удобно встраивать в программу. За счет небольшого размера они минимизируют и упрощают код. Но использование лямбда не является принципиальным. В Python 3 начинающим для работы будет достаточно инструкции def.
#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)Подобная функция не имеет названия, но её можно присвоить к переменной, которую в дальнейшем необходимо вызывать как обычную функцию.
На этой странице: def, return, docstrings, help (), функции возврата значения и voidФункции: основыДавайте откажемся от старой алгебры. Вы выучили «функции» примерно так:f (x) = x 2 + 1 В Python определение функции работает следующим образом. def — ключевое слово для определения функции. За именем функции следует параметр (ы) в (). Двоеточие: означает начало тела функции, которое отмечено отступом.Внутри тела функции оператор return определяет возвращаемое значение. После завершения определения функции вызов функции с аргументом возвращает значение.
Несколько параметров, строка документацииНиже представлена чуть более сложная функция.Он принимает два аргумента, имеет условное выражение в теле функции и начинается со строки:
Функции: Возврат vs.ПустотаВы можете подумать: «Погодите минутку, я не видел никакого оператора возврата в учебнике по определению функций». Вы правы — определенная Эд функция get_legal () не включала никакого оператора возврата, а только набор функций печати. В Python можно составить функцию без оператора возврата. Такие функции называются void , и они возвращают None, специальный объект Python «ничего». Вот пример функции void:
|
Определение функций в Python 3
Введение
Функция — это блок инструкций, который выполняет действие и, однажды определенный, может быть использован повторно. Функции делают код более модульным, позволяя использовать один и тот же код снова и снова.
Python имеет ряд встроенных функций, с которыми вы, возможно, знакомы, в том числе:
-
print (), который распечатает объект на терминал -
int (), который преобразует строковый или числовой тип данных в целочисленный тип данных -
len (), которая возвращает длину объекта
Имена функций включают круглые скобки и могут включать параметры.
В этом руководстве мы рассмотрим, как определять свои собственные функции для использования в проектах кодирования.
Определение функции
Давайте начнем с классического «Hello, World!» программу в функцию.
Мы создадим новый текстовый файл в выбранном текстовом редакторе и вызовем программу hello.py . Затем мы определим функцию.
Функция определяется с помощью ключевого слова def , за которым следует имя по вашему выбору, за которым следует набор круглых скобок, которые содержат любые параметры, которые функция примет (они могут быть пустыми), и заканчиваются двоеточием.
В этом случае мы определим функцию с именем hello () :
hello.py
def hello ():
Устанавливает начальную инструкцию для создания функции.
Отсюда мы добавим вторую строку с отступом из 4 пробелов, чтобы предоставить инструкции для того, что делает функция. В этом случае мы напечатаем Hello, World! к консоли:
hello.py
def hello ():
print ("Привет, мир!")
Наша функция теперь полностью определена, но если мы запустим программу на этом этапе, ничего не произойдет, поскольку мы не вызывали функцию.
Итак, вне нашего определенного функционального блока, давайте вызовем функцию с помощью hello () :
hello.py
def hello ():
print ("Привет, мир!")
Привет()
Теперь запустим программу:
Вы должны получить следующий результат:
Выход
Привет, мир!
Функции могут быть более сложными, чем функция hello () , которую мы определили выше. Например, мы можем использовать для циклов, условных операторов и т. Д. В нашем функциональном блоке.
Например, функция, определенная ниже, использует условный оператор, чтобы проверить, содержит ли вход для переменной name гласную, а затем использует цикл for для перебора букв в строке name .
names.py
# Определить имена функций ()
def names ():
# Настроить имя переменной с вводом
name = str (input ('Введите свое имя:'))
# Проверить, есть ли в имени гласная
если установлено ('aeiou'). пересечение (name.ниже()):
print ('Ваше имя содержит гласную.')
еще:
print ('Ваше имя не содержит гласных.')
# Итерировать по имени
на букву в имени:
печать (письмо)
# Вызов функции
имена ()
Функция names () , которую мы определили выше, устанавливает условный оператор и цикл для , показывая, как можно организовать код в определении функции. Однако, в зависимости от того, что мы намерены использовать в нашей программе и как мы хотим настроить наш код, мы можем захотеть определить условный оператор и цикл для как две отдельные функции.
Определение функций в программе делает наш код модульным и многоразовым, так что мы можем вызывать одни и те же функции, не переписывая их.
Работа с параметрами
До сих пор мы рассматривали функции с пустыми скобками, которые не принимают аргументов, но мы можем определять параметры в определениях функций в их скобках.
Параметр — это именованный объект в определении функции, указывающий аргумент, который функция может принимать.
Давайте создадим небольшую программу, которая принимает параметры x , y и z . Мы создадим функцию, которая складывает параметры вместе в различных конфигурациях. Их суммы будут напечатаны функцией. Затем мы вызовем функцию и передадим ей числа.
add_numbers.py
def add_numbers (x, y, z):
а = х + у
б = х + г
с = у + г
печать (a, b, c)
add_numbers (1, 2, 3)
Мы передали число 1 для параметра x , 2 для параметра y и 3 для параметра z .Эти значения соответствуют каждому параметру в том порядке, в котором они указаны.
Программа, по сути, выполняет следующую математику на основе значений, которые мы передали в параметры:
а = 1 + 2
б = 1 + 3
с = 2 + 3
Функция также печатает a , b и c , и на основании приведенных выше математических расчетов мы ожидаем, что a будет равно 3 , b будет равно 4 и c будет 5 .Запустим программу:
Выход
3 4 5
Когда мы передаем 1 , 2 и 3 в качестве параметров функции add_numbers () , мы получаем ожидаемый результат.
Параметры — это аргументы, которые обычно определяются как переменные в определениях функций. Им можно присвоить значения, когда вы запускаете метод, передавая аргументы функции.
Аргументы ключевого слова
В дополнение к вызову параметров по порядку вы можете использовать аргументов ключевого слова в вызове функции, в которой вызывающий абонент идентифицирует аргументы по имени параметра.
Когда вы используете аргументы ключевого слова, вы можете использовать параметры не по порядку, потому что интерпретатор Python будет использовать предоставленные ключевые слова для сопоставления значений с параметрами.
Давайте создадим функцию, которая будет показывать нам информацию профиля пользователя. Мы передадим ему параметры в виде имени пользователя (в виде строки) и подписчиков (в виде целого числа).
profile.py
# Определить функцию с параметрами
def profile_info (имя пользователя, подписчики):
print ("Имя пользователя:" + имя пользователя)
print ("Подписчики:" + str (подписчики))
В операторе определения функции имя пользователя и последователей содержатся в скобках функции profile_info () .Блок функции выводит информацию о пользователе в виде строк, используя два параметра.
Теперь мы можем вызвать функцию и назначить ей параметры:
profile.py
def profile_info (имя пользователя, подписчики):
print ("Имя пользователя:" + имя пользователя)
print ("Подписчики:" + str (подписчики))
# Вызов функции с параметрами, указанными выше
profile_info ("саммышарк", 945)
# Вызов функции с ключевыми аргументами
profile_info (username = "AlexAnglerfish", подписчики = 342)
В первом вызове функции мы ввели информацию с именем пользователя sammyshark и подписчиками 945 , во втором вызове функции мы использовали аргументы ключевого слова, присваивая значения переменным аргумента.
Запустим программу:
Выход
Имя пользователя: sammyshark
Подписчиков: 945
Имя пользователя: AlexAnglerfish
Подписчиков: 342
Вывод показывает нам имена пользователей и количество подписчиков для обоих пользователей.
Это также позволяет нам изменять порядок параметров, как в этом примере той же программы с другим вызовом:
profile.py
def profile_info (имя пользователя, подписчики):
print ("Имя пользователя:" + имя пользователя)
print ("Подписчики:" + str (подписчики))
# Изменить порядок параметров
profile_info (подписчики = 820, username = "cameron-catfish")
Когда мы снова запустим программу с профилем python .py , мы получим следующий результат:
Выход
Имя пользователя: cameron-catfish
Подписчиков: 820
Поскольку определение функции поддерживает тот же порядок print () операторов, если мы используем аргументы ключевого слова, не имеет значения, в каком порядке мы передаем их в вызов функции.
Значения аргументов по умолчанию
Мы также можем предоставить значения по умолчанию для одного или обоих параметров. Давайте создадим значение по умолчанию для параметра подписчиков со значением 1 :
профиль.py
def profile_info (имя пользователя, подписчики = 1):
print ("Имя пользователя:" + имя пользователя)
print ("Подписчики:" + str (подписчики))
Теперь мы можем запускать функцию только с назначенной функцией имени пользователя, и количество подписчиков автоматически по умолчанию будет равно 1. Мы также можем изменить количество подписчиков, если захотим.
profile.py
def profile_info (имя пользователя, подписчики = 1):
print ("Имя пользователя:" + имя пользователя)
print ("Подписчики:" + str (подписчики))
profile_info (username = "JOctopus")
profile_info (username = "sammyshark", подписчики = 945)
Когда мы запускаем программу с профилем python .py , мы получим следующий результат:
Выход
Имя пользователя: JOctopus
Подписчиков: 1
Имя пользователя: sammyshark
Подписчиков: 945
Предоставление параметров по умолчанию со значениями может позволить нам пропустить определение значений для каждого аргумента, который уже имеет значение по умолчанию.
Возвращение значения
Вы можете передать значение параметра в функцию, и функция также может создавать значение.
Функция может генерировать значение с помощью оператора return , который завершает функцию, и , при необходимости, передает выражение обратно вызывающей стороне.Если вы используете оператор return без аргументов, функция вернет None .
До сих пор мы использовали оператор print () вместо оператора return в наших функциях. Давайте создадим программу, которая вместо печати будет возвращать переменную.
В новом текстовом файле с именем square.py мы создадим программу, которая возводит в квадрат параметр x и возвращает переменную y . Мы вызываем вызов для печати переменной result , которая формируется путем выполнения функции square () с переданным ей 3 .
square.py
def square (x):
у = х ** 2
вернуть y
результат = квадрат (3)
печать (результат)
Мы можем запустить программу и увидеть результат:
Выход
9
Целое число 9 возвращается как результат, чего мы и ожидаем, если попросим Python найти квадрат 3.
Чтобы лучше понять, как работает оператор return , мы можем закомментировать оператор return в программе:
кв.py
def квадрат (x):
у = х ** 2
# return y
результат = квадрат (3)
печать (результат)
Теперь давайте снова запустим программу:
Выход
Нет
Без использования здесь оператора return программа не может вернуть значение, поэтому по умолчанию используется значение None .
В качестве другого примера, в приведенной выше программе add_numbers.py мы могли заменить оператор print () на оператор return .
add_numbers.py
def add_numbers (x, y, z):
а = х + у
б = х + г
с = у + г
вернуть a, b, c
суммы = add_numbers (1, 2, 3)
печать (суммы)
Вне функции мы устанавливаем переменную sums равной результату функции, принимающей 1 , 2 и 3 , как мы делали выше. Затем мы вызвали печать переменной сумм .
Давайте снова запустим программу, теперь у нее есть оператор return :
Выход
(3, 4, 5)
Мы получаем те же числа 3 , 4 и 5 в качестве выходных данных, которые мы получили ранее с помощью оператора print () в функции.На этот раз он доставляется в виде кортежа, потому что в списке выражений оператора return есть как минимум одна запятая.
завершаются немедленно, когда они попадают в оператор return , независимо от того, возвращают они значение или нет.
return_loop.py
def loop_five ():
для x в диапазоне (0, 25):
печать (х)
если x == 5:
# Остановить функцию при x == 5
возвращаться
print («Эта строка не будет выполняться.»)
loop_five ()
Использование оператора return в цикле для завершает функцию, поэтому строка, которая находится вне цикла, не будет выполняться.Если бы вместо этого мы использовали оператор break , в это время завершился бы только цикл, и была бы запущена последняя строка print () .
Оператор возврата завершает функцию и может возвращать значение при запуске с параметром.
Использование
main () как функции Хотя в Python вы можете вызвать функцию внизу своей программы, и она запустится (как мы это делали в приведенных выше примерах), для многих языков программирования (таких как C ++ и Java) для выполнения требуется основная функция .Включение функции main () , хотя и не обязательно, может логически структурировать наши программы Python, объединяя наиболее важные компоненты программы в одну функцию. Это также может облегчить чтение наших программ программистам, не использующим Python.
Начнем с добавления функции main () в программу hello.py , описанную выше. Мы сохраним нашу функцию hello () , а затем определим функцию main () :
hello.py
def hello ():
print ("Привет, мир!")
def main ():
Давайте включим в функцию main () инструкцию print () , чтобы сообщить нам, что мы находимся в функции main () .Кроме того, давайте вызовем функцию hello () внутри функции main () :
hello.py
def hello ():
print ("Привет, мир!")
def main ():
print («Это основная функция»)
Привет()
Наконец, внизу программы мы вызовем функцию main () :
hello.py
def hello ():
print ("Привет, мир!")
def main ():
print ("Это основная функция.")
Привет()
основной()
На этом этапе мы можем запустить нашу программу:
Мы получим следующий результат:
Выход
Это основная функция.Привет мир!
Так как мы вызвали функцию hello () внутри main () , а затем вызвали только main () для запуска, Hello, World! Текст печатается только один раз после строки, которая сообщает нам, что мы находимся в основной функции.
Далее мы собираемся работать с несколькими функциями, поэтому стоит рассмотреть область видимости глобальных и локальных переменных. Если вы определяете переменную в функциональном блоке, вы сможете использовать эту переменную только в этой функции.Если вы хотите использовать переменные в функциях, возможно, лучше объявить глобальную переменную.
В Python '__main__' — это имя области, в которой будет выполняться код верхнего уровня. Когда программа запускается из стандартного ввода, сценария или из интерактивной подсказки, ее __name__ устанавливается равным '__main__' .
По этой причине существует соглашение об использовании следующей конструкции:
, если __name__ == '__main__':
# Код для запуска, если это основная программа
Это позволяет использовать программные файлы:
- в качестве основной программы и выполнить то, что следует за оператором
if - как модуль и не запускать то, что следует за оператором
if.
Любой код, не содержащийся в этом операторе, будет выполнен при запуске. Если вы используете свой программный файл в качестве модуля, код, которого нет в этом операторе, также будет выполняться при его импорте при запуске вторичного файла.
Давайте расширим нашу программу names.py выше и создадим новый файл с именем more_names.py . В этой программе мы объявим глобальную переменную и изменим нашу исходную функцию names () так, чтобы инструкции содержались в двух дискретных функциях.
Первая функция has_vowel () проверяет, содержит ли строка имени гласную.
Вторая функция print_letters () будет печатать каждую букву строки name .
more_names.py
# Объявить имя глобальной переменной для использования во всех функциях
name = str (input ('Введите свое имя:'))
# Определить функцию, чтобы проверить, содержит ли имя гласную
def has_vowel ():
если установлено ('aeiou'). пересечение (name.ниже()):
print ('Ваше имя содержит гласную.')
еще:
print ('Ваше имя не содержит гласных.')
# Перебрать буквы в строке имени
def print_letters ():
на букву в имени:
печать (письмо)
С такой настройкой давайте определим функцию main () , которая будет содержать вызов как функций has_vowel () , так и print_letters () .
more_names.py
# Объявить имя глобальной переменной для использования во всех функциях
name = str (input ('Введите свое имя:'))
# Определить функцию, чтобы проверить, содержит ли имя гласную
def has_vowel ():
если установлено ('aeiou').пересечение (name.lower ()):
print ('Ваше имя содержит гласную.')
еще:
print ('Ваше имя не содержит гласных.')
# Перебрать буквы в строке имени
def print_letters ():
на букву в имени:
печать (письмо)
# Определить основной метод, который вызывает другие функции
def main ():
has_vowel ()
print_letters ()
Наконец, мы добавим конструкцию if __name__ == '__main__': в конец файла. Для наших целей, поскольку мы поместили все функции, которые мы хотели бы выполнять, в функцию main () , мы вызовем функцию main () после этого оператора if .
more_names.py
# Объявить имя глобальной переменной для использования во всех функциях
name = str (input ('Введите свое имя:'))
# Определить функцию, чтобы проверить, содержит ли имя гласную
def has_vowel ():
если установлено ('aeiou'). пересечение (name.lower ()):
print ('Ваше имя содержит гласную.')
еще:
print ('Ваше имя не содержит гласных.')
# Перебрать буквы в строке имени
def print_letters ():
на букву в имени:
печать (письмо)
# Определить основной метод, который вызывает другие функции
def main ():
has_vowel ()
print_letters ()
# Выполнить функцию main ()
если __name__ == '__main__':
основной()
Теперь мы можем запустить программу:
Программа покажет тот же результат, что и имена .py , но здесь код более организован и может использоваться по модульному принципу без изменений.
Если вы не хотите объявлять функцию main () , вы также могли завершить программу следующим образом:
more_names.py
...
если __name__ == '__main__':
has_vowel ()
print_letters ()
Использование main () в качестве функции и оператора if __name__ == '__main__': может организовать ваш код логическим образом, делая его более читаемым и модульным.
Заключение
Функции — это блоки кода инструкций, которые выполняют действия в программе, помогая сделать наш код многоразовым и модульным.
Чтобы узнать больше о том, как сделать ваш код более модульным, вы можете прочитать наше руководство по написанию модулей на Python 3.
Определение вашей собственной функции Python — Настоящий Python
На протяжении предыдущих руководств этой серии вы видели множество примеров, демонстрирующих использование встроенных функций Python.В этом руководстве вы узнаете, как определить вашу собственную функцию Python . Вы узнаете, когда разделить вашу программу на отдельные пользовательские функции и какие инструменты вам понадобятся для этого.
Из этого руководства вы узнаете:
- Как функции работают в Python и почему они полезны
- Как определить и вызвать вашу собственную функцию Python
- Механизмы передачи аргументов вашей функции
- Как вернуть данные из вашей функции обратно в вызывающую среду
Функции в Python
Возможно, вы знакомы с математической концепцией функции .Функция — это связь или отображение между одним или несколькими входами и набором выходов. В математике функция обычно представлена так:
Здесь f — это функция, которая работает на входах x и y . Результатом функции будет z . Однако функции программирования гораздо более обобщены и универсальны, чем это математическое определение. Фактически, правильное определение и использование функций настолько критично для правильной разработки программного обеспечения, что практически все современные языки программирования поддерживают как встроенные, так и определяемые пользователем функции.
В программировании функция — это автономный блок кода, который инкапсулирует конкретную задачу или связанную группу задач. В предыдущих руководствах этой серии вы познакомились с некоторыми встроенными функциями, предоставляемыми Python. id () , например, принимает один аргумент и возвращает уникальный целочисленный идентификатор этого объекта:
>>> s = 'foobar'
>>> id (s)
56313440
len () возвращает длину переданного ему аргумента:
>>> a = ['foo', 'bar', 'baz', 'qux']
>>> len (а)
4
any () принимает в качестве аргумента итерацию и возвращает Истина, , если какой-либо из элементов в итерации является истинным, и Ложь, в противном случае:
>>> любое ([Ложь, Ложь, Ложь])
Ложь
>>> любой ([Ложь, Истина, Ложь])
Правда
>>> any (['bar' == 'baz', len ('foo') == 4, 'qux' в {'foo', 'bar', 'baz'}])
Ложь
>>> any (['bar' == 'baz', len ('foo') == 3, 'qux' in {'foo', 'bar', 'baz'}])
Правда
Каждая из этих встроенных функций выполняет определенную задачу.Код, выполняющий задачу, где-то определен, но вам не нужно знать, где и даже как он работает. Все, что вам нужно знать, это интерфейс функции:
- Какие аргументов (если есть) нужно
- Какие значения (если есть) он возвращает
Затем вы вызываете функцию и передаете ей соответствующие аргументы. Выполнение программы переходит к обозначенному фрагменту кода и делает свое полезное дело. Когда функция завершена, выполнение возвращается к вашему коду с того места, где оно было остановлено.Функция может возвращать или не возвращать данные для использования вашим кодом, как в приведенных выше примерах.
Когда вы определяете свою собственную функцию Python, она работает точно так же. Где-то в коде вы вызываете функцию Python, и выполнение программы передается в тело кода, составляющего функцию.
Примечание: В этом случае вы будете знать, где находится код и как именно он работает, потому что вы его написали!
Когда функция завершена, выполнение возвращается в то место, где функция была вызвана.В зависимости от того, как вы разработали интерфейс функции, данные могут передаваться при вызове функции, а возвращаемые значения могут передаваться обратно после ее завершения.
Важность функций Python
Практически все языки программирования, используемые сегодня, поддерживают определенные пользователем функции, хотя их не всегда называют функциями. На других языках вы можете встретить их как одно из следующих:
- Подпрограммы
- Процедуры
- Методы
- Подпрограммы
Итак, зачем вообще определять функции? Есть несколько очень веских причин.Давайте пройдемся по нескольким.
Абстракция и возможность повторного использования
Предположим, вы пишете код, который делает что-то полезное. По мере продолжения разработки вы обнаружите, что задача, выполняемая этим кодом, вам часто требуется во многих различных местах вашего приложения. Что вы должны сделать? Что ж, вы можете просто копировать код снова и снова, используя возможность копирования и вставки вашего редактора.
Позже вы, вероятно, решите, что рассматриваемый код нужно изменить.Вы либо обнаружите, что с ним что-то не так, что нужно исправить, либо захотите как-то улучшить его. Если копии кода разбросаны по всему приложению, вам нужно будет внести необходимые изменения в каждом месте.
Примечание: На первый взгляд это может показаться разумным решением, но в долгосрочной перспективе это может стать кошмаром для обслуживания! Хотя ваш редактор кода может помочь, предоставляя функцию поиска и замены, этот метод подвержен ошибкам, и вы можете легко внести в свой код ошибки, которые будет трудно найти.
Лучшее решение — определить функцию Python, которая выполняет задачу . В любом месте вашего приложения, где вам нужно выполнить задачу, вы просто вызываете функцию. В дальнейшем, если вы решите изменить способ его работы, вам нужно будет изменить код только в одном месте, то есть в том месте, где определена функция. Изменения будут автоматически приняты везде, где вызывается функция.
Абстракция функциональности в определение функции является примером принципа «не повторяйся» (DRY) при разработке программного обеспечения.Это, пожалуй, самая сильная мотивация для использования функций.
Модульность
Функциипозволяют разбивать сложные процессы на более мелкие шаги. Представьте, например, что у вас есть программа, которая читает файл, обрабатывает его содержимое, а затем записывает выходной файл. Ваш код может выглядеть так:
# Основная программа
# Код для чтения файла в
<заявление>
<заявление>
<заявление>
<заявление>
# Код для обработки файла
<заявление>
<заявление>
<заявление>
<заявление>
# Код для записи файла
<заявление>
<заявление>
<заявление>
<заявление>
В этом примере основная программа — это связка кода, связанного в длинную последовательность, с пробелами и комментариями, которые помогают упорядочить ее.Однако если бы код стал намного длиннее и сложнее, вам было бы все труднее осмысливать его.
В качестве альтернативы вы можете структурировать код примерно так:
def read_file ():
# Код для чтения файла в
<заявление>
<заявление>
<заявление>
<заявление>
def process_file ():
# Код для обработки файла
<заявление>
<заявление>
<заявление>
<заявление>
def write_file ():
# Код для записи файла
<заявление>
<заявление>
<заявление>
<заявление>
# Основная программа
read_file ()
process_file ()
write_file ()
Этот пример — модульный .Вместо того, чтобы связывать весь код вместе, он разбит на отдельные функции, каждая из которых ориентирована на конкретную задачу. Эти задачи: чтение , процесс и запись . Теперь основной программе просто нужно вызвать каждый из них по очереди.
Примечание: Ключевое слово def вводит новое определение функции Python. Вы все об этом узнаете очень скоро.
В жизни вы делаете такие вещи все время, даже если не думаете об этом явно.Если бы вы захотели переместить несколько полок, заполненных вещами, из одной стороны гаража в другую, то, надеюсь, вы не станете просто стоять и бесцельно думать: «Ой, блин. Мне нужно перевезти туда все это! Как я это сделал???» Вы бы разделили работу на управляемые шаги:
- Уберите все с полок.
- Разобрать полки.
- Перенесите части полки через гараж на новое место.
- Снова соберите полки.
- Отнесите вещи через гараж.
- Положите вещей обратно на полки.
Разделение большой задачи на более мелкие и небольшие подзадачи помогает упростить рассмотрение большой задачи и управление ею. По мере того, как программы становятся более сложными, становится все более выгодным модулировать их таким образом.
Разделение пространств имен
Пространство имен — это область программы, в которой идентификаторов имеют значение.Как вы увидите ниже, при вызове функции Python для этой функции создается новое пространство имен, которое отличается от всех других уже существующих пространств имен.
Практический результат этого состоит в том, что переменные можно определять и использовать в функции Python, даже если они имеют то же имя, что и переменные, определенные в других функциях или в основной программе. В этих случаях не будет путаницы или вмешательства, потому что они хранятся в отдельных пространствах имен.
Это означает, что, когда вы пишете код внутри функции, вы можете использовать имена и идентификаторы переменных, не беспокоясь о том, использовались ли они уже где-то за пределами функции.Это помогает значительно минимизировать ошибки в коде.
Надеюсь, вы достаточно убедились в достоинствах функций и хотите их создать! Посмотрим как.
Вызовы функций и определение
Обычный синтаксис для определения функции Python следующий:
def <имя_функции> ([<параметры>]):
<заявление (я)>
Компоненты определения объяснены в таблице ниже:
| Компонент | Значение |
|---|---|
деф | Ключевое слово, информирующее Python о том, что функция определяется |
<имя_функции> | Действительный идентификатор Python, который называет функцию |
<параметры> | Необязательный список параметров, разделенных запятыми, которые могут быть переданы функции |
: | Знаки пунктуации, обозначающие конец заголовка функции Python (имя и список параметров) |
<выписки> | Блок действительных операторов Python |
Последний элемент, , называется телом функции.Тело — это блок операторов, который будет выполняться при вызове функции. Тело функции Python определяется отступом в соответствии с правилом off-side. Это то же самое, что и кодовые блоки, связанные со структурой управления, например, , если, или , а .
Синтаксис для вызова функции Python следующий:
<имя_функции> ([<аргументы>])
<аргументы> — значения, переданные в функцию.Они соответствуют <параметры> в определении функции Python. Вы можете определить функцию, которая не принимает никаких аргументов, но круглые скобки по-прежнему необходимы. И определение функции, и вызов функции всегда должны включать круглые скобки, даже если они пусты.
Как обычно, вы начнете с небольшого примера, а затем добавите сложности. Помня освященную веками математическую традицию, вы вызовете свою первую функцию Python f () . Вот файл сценария foo.py , который определяет и вызывает f () :
1def f ():
2 s = '- Внутри f ()'
3 отпечатка (ов)
4
5print ('Перед вызовом f ()')
6f ()
7print ('После вызова f ()')
Вот как работает этот код:
Строка 1 использует ключевое слово
def, чтобы указать, что функция определяется. Выполнение оператораdefпросто создает определениеf (). Все следующие строки с отступом (строки 2–3) становятся частью телаf ()и сохраняются как его определение, но еще не выполнены.Строка 4 — это небольшой пробел между определением функции и первой строкой основной программы. Хотя это и не является синтаксически необходимым, иметь его приятно. Чтобы узнать больше о пробелах вокруг определений функций Python верхнего уровня, ознакомьтесь с разделом «Написание красивого кода Python с помощью PEP 8.
».Строка 5 — это первый оператор без отступа, потому что он не является частью определения
f (). Это начало основной программы.Когда выполняется основная программа, этот оператор выполняется первым.Линия 6 — это звонок на
f (). Обратите внимание, что пустые круглые скобки всегда требуются как в определении функции, так и при ее вызове, даже если нет параметров или аргументов. Выполнение переходит кf (), и выполняются операторы в телеf ().Строка 7 — это следующая строка, выполняемая после завершения тела
f ().Выполнение возвращается к этому операторуprint ().
Последовательность выполнения (или поток управления ) для foo.py показана на следующей диаграмме:
Когда foo.py запускается из командной строки Windows, результат будет следующим:
C: \ Users \ john \ Documents \ Python \ doc> python foo.py
Перед вызовом f ()
- Внутри f ()
После вызова f ()
Иногда вам может понадобиться определить пустую функцию, которая ничего не делает.Это называется заглушкой , которая обычно является временным заполнителем для функции Python, которая будет полностью реализована позже. Как блок в управляющей структуре не может быть пустым, так и тело функции не может быть пустым. Чтобы определить функцию-заглушку, используйте оператор pass :
>>> def f ():
... проходить
...
>>> f ()
Как видно выше, вызов функции-заглушки синтаксически допустим, но ничего не делает.
Передан аргумент
До сих пор в этом руководстве функции, которые вы определили, не принимали никаких аргументов. Иногда это может быть полезно, и иногда вы будете писать такие функции. Однако чаще вы хотите, чтобы передавал данные в функцию , чтобы ее поведение могло изменяться от одного вызова к другому. Посмотрим, как это сделать.
Позиционные аргументы
Самый простой способ передать аргументы функции Python — использовать позиционных аргументов (также называемых обязательных аргументов ).В определении функции вы указываете в скобках список параметров, разделенных запятыми:
>>> >>> def f (кол-во, шт., Цена):
... print (f '{qty} {item} cost $ {price: .2f}')
...
При вызове функции вы указываете соответствующий список аргументов:
>>> >>> f (6, 'бананы', 1.74)
6 бананов стоят 1,74 доллара
Параметры ( кол-во , предмет и цена ) ведут себя как переменные , которые определены локально для функции.Когда функция вызывается, переданные аргументы ( 6 , 'бананы' и 1,74 ) привязывают к параметрам по порядку, как если бы путем присвоения переменной:
| Параметр | Аргумент | |
|---|---|---|
шт. | ← | 6 |
товар | ← | бананы |
цена | ← | 1.74 |
В некоторых текстах по программированию параметры, указанные в определении функции, называются формальными параметрами , а аргументы в вызове функции называются фактическими параметрами :
Хотя позиционные аргументы являются наиболее простым способом передачи данных в функцию, они также обеспечивают наименьшую гибкость. Во-первых, порядок аргументов в вызове должен соответствовать порядку параметров в определении.Конечно, ничто не мешает вам указывать позиционные аргументы не по порядку:
>>> >>> f ('бананы', 1.74, 6)
бананы 1.74 стоят $ 6.00
Функция может даже работать, как в приведенном выше примере, но маловероятно, что она даст правильные результаты. Это ответственность программиста, который определяет функцию, чтобы задокументировать, какими должны быть соответствующие аргументы , и ответственность пользователя функции — знать эту информацию и соблюдать ее.
С позиционными аргументами аргументы в вызове и параметры в определении должны согласовываться не только по порядку, но и по номеру . По этой причине позиционные аргументы также называются обязательными аргументами. Вы не можете ничего пропустить при вызове функции:
>>> >>> # Слишком мало аргументов
>>> f (6, 'бананы')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
f (6, 'бананы')
TypeError: f () отсутствует 1 обязательный позиционный аргумент: 'цена'
Вы также не можете указать дополнительные:
>>> >>> # Слишком много аргументов
>>> f (6, 'бананы', 1.74, кумкваты)
Отслеживание (последний вызов последний):
Файл "", строка 1, в
f (6, «бананы», 1,74, «кумкват»)
TypeError: f () принимает 3 позиционных аргумента, но было дано 4
Позиционные аргументы концептуально просты в использовании, но они не очень снисходительны. Вы должны указать такое же количество аргументов в вызове функции, как и параметры в определении, и в точно таком же порядке. В следующих разделах вы увидите некоторые приемы передачи аргументов, которые снимают эти ограничения.
Аргументы ключевого слова
При вызове функции можно указать аргументы в форме <ключевое слово> = <значение> . В этом случае каждое <ключевое слово> должно соответствовать параметру в определении функции Python. Например, ранее определенная функция f () может быть вызвана с аргументами ключевого слова следующим образом:
>>> f (qty = 6, item = 'bananas', price = 1,74)
6 бананов стоят 1,74 доллара
Ссылка на ключевое слово, не соответствующее ни одному из заявленных параметров, генерирует исключение:
>>> >>> f (qty = 6, item = 'bananas', cost = 1.74)
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: f () получил неожиданный аргумент ключевого слова 'стоимость'
Использование аргументов ключевого слова снимает ограничение на порядок аргументов. Каждый аргумент ключевого слова явно обозначает конкретный параметр по имени, поэтому вы можете указать их в любом порядке, и Python все равно будет знать, какой аргумент соответствует какому параметру:
>>> >>> f (item = 'bananas', price = 1.74, qty = 6)
6 бананов стоят 1 доллар.74
Однако, как и в случае с позиционными аргументами, количество аргументов и параметров должно совпадать:
>>> >>> # Еще слишком мало аргументов
>>> f (кол-во = 6, элемент = 'бананы')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
f (кол-во = 6, элемент = 'бананы')
TypeError: f () отсутствует 1 обязательный позиционный аргумент: 'цена'
Итак, аргументы ключевого слова допускают гибкость в порядке указания аргументов функции, но количество аргументов остается жестким.
Вы можете вызвать функцию, используя как позиционные, так и ключевые аргументы:
>>> >>> f (6, price = 1.74, item = 'bananas')
6 бананов стоят 1,74 доллара
>>> f (6, 'бананы', цена = 1,74)
6 бананов стоят 1,74 доллара
Когда присутствуют как позиционные аргументы, так и аргументы ключевого слова, все позиционные аргументы должны идти первыми:
>>> >>> f (6, item = 'bananas', 1.74)
SyntaxError: позиционный аргумент следует за аргументом ключевого слова
После того, как вы указали аргумент ключевого слова, справа от него не может быть никаких позиционных аргументов.
Параметры по умолчанию
Если параметр, указанный в определении функции Python, имеет форму <имя> = <значение> , тогда <значение> становится значением по умолчанию для этого параметра. Параметры, определенные таким образом, называются параметрами по умолчанию или дополнительными параметрами . Пример определения функции с параметрами по умолчанию показан ниже:
>>> def f (qty = 6, item = 'bananas', price = 1.74):
... print (f '{qty} {item} cost $ {price :.2f} ')
...
Когда вызывается эта версия f () , любой аргумент, который не указан, принимает значение по умолчанию:
>>> f (4, 'яблоки', 2.24)
4 яблока стоят 2,24 доллара.
>>> f (4, 'яблоки')
4 яблока стоят 1,74 доллара
>>> f (4)
4 банана стоят 1,74 доллара
>>> f ()
6 бананов стоят 1,74 доллара
>>> f (item = 'кумкват', кол-во = 9)
9 кумкватов стоят 1,74 доллара
>>> f (цена = 2,29)
6 бананов стоят 2,29 доллара
Итого:
- Позиционные аргументы должны соответствовать по порядку и номеру параметрам, объявленным в определении функции.
- Аргументы ключевого слова должны совпадать с объявленными параметрами по количеству, но они могут быть указаны в произвольном порядке.
- Параметры по умолчанию позволяют опускать некоторые аргументы при вызове функции.
Изменяемые значения параметров по умолчанию
Все может стать странным, если вы укажете значение параметра по умолчанию, которое является изменяемым объектом . Рассмотрим это определение функции Python:
>>> >>> def f (my_list = []):
... my_list.append ('###')
... вернуть my_list
...
f () принимает единственный параметр списка, добавляет строку '###' в конец списка и возвращает результат:
>>> f (['foo', 'bar', 'baz'])
['foo', 'bar', 'baz', '###']
>>> f ([1, 2, 3, 4, 5])
[1, 2, 3, 4, 5, '###']
Значение по умолчанию для параметра my_list — это пустой список, поэтому, если f () вызывается без каких-либо аргументов, возвращаемое значение представляет собой список с одним элементом '###' :
Пока все имеет смысл.Итак, что вы ожидаете, если f () вызывается без каких-либо параметров второй и третий раз? Посмотрим:
>>> f ()
['###', '###']
>>> f ()
['###', '###', '###']
Ой! Вы могли ожидать, что каждый последующий вызов также будет возвращать одноэлементный список ['###'] , как и первый. Вместо этого возвращаемое значение продолжает расти. Что случилось?
В Python значениями параметров по умолчанию являются , определенные только один раз, , когда функция определена (то есть, когда выполняется инструкция def ).Значение по умолчанию не переопределяется каждый раз при вызове функции. Таким образом, каждый раз, когда вы вызываете f () без параметра, вы выполняете .append () в том же списке.
Вы можете продемонстрировать это с помощью id () :
>>> def f (my_list = []):
... печать (id (my_list))
... my_list.append ('###')
... вернуть my_list
...
>>> f ()
140095566958408
['###']
>>> f ()
140095566958408
['###', '###']
>>> f ()
140095566958408
['###', '###', '###']
Отображаемый идентификатор объекта подтверждает, что, когда для my_list разрешено использовать значение по умолчанию, значение будет одним и тем же объектом при каждом вызове.Поскольку списки изменяемы, каждый последующий вызов .append () заставляет список удлиняться. Это распространенная и довольно хорошо задокументированная ошибка, когда вы используете изменяемый объект в качестве значения параметра по умолчанию. Это потенциально приводит к путанице в поведении кода, и, вероятно, лучше этого избегать.
В качестве обходного пути рассмотрите возможность использования значения аргумента по умолчанию, которое сигнализирует , что аргумент не указан . Практически любое значение будет работать, но Нет. — это обычный выбор. Когда значение дозорного показывает, что аргумент не задан, создайте новый пустой список внутри функции:
>>> def f (my_list = None):
... если my_list - None:
... my_list = []
... my_list.append ('###')
... вернуть my_list
...
>>> f ()
['###']
>>> f ()
['###']
>>> f ()
['###']
>>> f (['фу', 'бар', 'баз'])
['foo', 'bar', 'baz', '###']
>>> f ([1, 2, 3, 4, 5])
[1, 2, 3, 4, 5, '###']
Обратите внимание, как это гарантирует, что my_list теперь действительно будет по умолчанию использовать пустой список всякий раз, когда f () вызывается без аргумента.
Передача по значению и передача по ссылке в Pascal
В разработке языков программирования есть две общие парадигмы для передачи аргумента функции:
- Передаваемое значение: Копия аргумента передается функции.
- Передача по ссылке: Ссылка на аргумент передается функции.
Существуют и другие механизмы, но они по сути являются вариациями этих двух. В этом разделе вы сделаете небольшой отход от Python и кратко рассмотрите Pascal, язык программирования, который делает особенно четкое различие между этими двумя.
Примечание: Не волнуйтесь, если вы не знакомы с Паскалем! Концепции аналогичны концепциям Python, а показанные примеры сопровождаются достаточно подробным объяснением, чтобы вы могли составить общее представление.Как только вы увидели, как передача аргументов работает в Паскале, мы вернемся к Python, и вы увидите, как он сравнивается.
Вот что вам нужно знать о синтаксисе Паскаля:
- Процедуры: Процедура в Паскале похожа на функцию Python.
- Двоеточие равно: Этот оператор (
: =) используется для присваивания в Паскале. Это аналог знака равенства (=) в Python. -
Writeln (): Эта функция отображает данные на консоли, аналогично функции print () в Python.
Имея такую основу, вот первый пример Pascal:
1 // Пример Pascal # 1
2
3процедура f (fx: целое число);
4начать
5 Writeln ('Начать f (): fx =', fx);
6 FX: = 10;
7 Writeln ('Конец f (): fx =', fx);
8end;
9
10 // Основная программа
11вар
12 x: целое число;
13
14начало
15 х: = 5;
16 Writeln ('Перед f (): x =', x);
17 f (x);
18 Writeln ('После f (): x =', x);
19 конец.
Вот что происходит:
- Строка 12: Основная программа определяет целочисленную переменную
x. - Строка 15: Первоначально
xприсваивается значение5. - Строка 17: Затем он вызывает процедуру
f (), передаваяxв качестве аргумента. - Строка 5: Внутри
f ()инструкцияwriteln ()показывает, что соответствующий параметрfxизначально равен5, переданному значению. - Строка 6:
fxзатем присваивается значение10. - Строка 7: Это значение проверяется этим оператором
writeln (), выполняемым непосредственно перед завершением работыf (). - Строка 18: Вернувшись в вызывающую среду основной программы, этот оператор
writeln ()показывает, что после возвратаf ()xвсе еще остается5, как это было до вызова процедуры. .
Выполнение этого кода приводит к следующему результату:
Перед f (): x = 5
Начать f (): fx = 5
Конец f (): fx = 10
После f (): x = 5
В этом примере x — это , переданное по значению , поэтому f () принимает только копию.Когда соответствующий параметр fx изменяется, x не изменяется.
Примечание: Если вы хотите увидеть это в действии, вы можете запустить код самостоятельно, используя онлайн-компилятор Pascal.
Просто выполните следующие действия:
- Скопируйте код из поля кода выше.
- Посетите онлайн-компилятор Паскаля.
- В поле кода слева замените любое существующее содержимое кодом, который вы скопировали на шаге 1.
- Щелкните Выполнить .
Вы должны увидеть тот же результат, что и выше.
Теперь сравните это со следующим примером:
1 // Пример # 2 на Паскале
2
3процедура f (var fx: integer);
4начать
5 Writeln ('Начать f (): fx =', fx);
6 FX: = 10;
7 Writeln ('Конец f (): fx =', fx);
8end;
9
10 // Основная программа
11вар
12 x: целое число;
13
14начало
15 х: = 5;
16 Writeln ('Перед f (): x =', x);
17 f (x);
18 Writeln ('После f (): x =', x);
19 конец.
Этот код идентичен первому примеру с одним изменением.Это наличие слова var перед fx в определении процедуры f () в строке 3. Это означает, что аргумент для f () — это , переданный по ссылке . Изменения, внесенные в соответствующий параметр fx , также изменят аргумент в вызывающей среде.
Вывод этого кода такой же, как и раньше, за исключением последней строки:
Перед f (): x = 5
Начать f (): fx = 5
Конец f (): fx = 10
После f (): x = 10
Опять же, fx присваивается значение 10 внутри f () , как и раньше.Но на этот раз, когда возвращается f () , x в основной программе также было изменено.
Во многих языках программирования это, по сути, различие между передачей по значению и передачей по ссылке:
- Если переменная передается по значению, , тогда у функции есть копия для работы, но она не может изменить исходное значение в вызывающей среде.
- Если переменная передается по ссылке, , то любые изменения, которые функция вносит в соответствующий параметр, повлияют на значение в вызывающей среде.
Причина, по которой «почему» происходит от того, что означает ссылка на этих языках. Значения переменных хранятся в памяти. В Паскале и подобных языках ссылка — это, по сути, адрес этой ячейки памяти, как показано ниже:
На схеме слева x — это память, выделенная в пространстве имен основной программы. Когда вызывается f () , x — это , переданное значением , поэтому память для соответствующего параметра fx выделяется в пространстве имен f () , а значение x копируется туда. .Когда f () изменяет fx , изменяется именно эта локальная копия. Значение x в вызывающей среде остается неизменным.
На диаграмме справа x — это , переданное по ссылке . Соответствующий параметр fx указывает на фактический адрес в пространстве имен основной программы, где хранится значение x . Когда f () изменяет fx , он изменяет значение в этом месте, точно так же, как если бы основная программа изменяла саму x .
Передача по значению и передача по ссылке в Python
Являются ли параметры в Python передачей по значению или по ссылке? Ответ таков: ни то, ни другое. Это потому, что ссылка в Python означает не совсем то же самое, что в Паскале.
Напомним, что в Python каждая часть данных представляет собой объект . Ссылка указывает на объект, а не на конкретную ячейку памяти. Это означает, что присваивание не интерпретируется в Python так же, как в Паскале. Рассмотрим следующую пару операторов в Паскале:
Они интерпретируются следующим образом:
- Переменная
xссылается на конкретную ячейку памяти. - Первый оператор помещает в это место значение
5. - Следующий оператор перезаписывает
5и помещает вместо него10.
Напротив, в Python аналогичные операторы присваивания выглядят следующим образом:
Эти операторы присваивания имеют следующее значение:
- Первый оператор заставляет
xуказывать на объект, значение которого составляет5. - Следующий оператор переназначает
xкак новую ссылку на другой объект, значение которого равно10. Другими словами, второе присвоение повторно привязываетxк другому объекту со значением10.
В Python, когда вы передаете аргумент функции, происходит аналогичное повторное связывание . Рассмотрим этот пример:
>>> 1 >>> def f (fx):
2 ... fx = 10
3 ...
4 >>> х = 5
5 >>> f (x)
6 >>> х
75
В основной программе инструкция x = 5 в строке 5 создает ссылку с именем x , привязанную к объекту, значение которого равно 5 .Затем в строке 7 вызывается f () с x в качестве аргумента. При первом запуске f () создается новая ссылка с именем fx , которая изначально указывает на тот же объект 5 , что и x :
Однако, когда выполняется инструкция fx = 10 в строке 2, f () выполняет повторную привязку fx к новому объекту, значение которого равно 10 . Две ссылки, x и fx , не связаны друг с другом .Больше ничего из того, что делает f () , не повлияет на x , и когда f () завершится, x по-прежнему будет указывать на объект 5 , как это было до вызова функции:
Все это можно подтвердить с помощью id () . Вот слегка расширенная версия приведенного выше примера, которая отображает числовые идентификаторы задействованных объектов:
1 >>> def f (fx): 2 ... печать ('fx =', fx, '/ id (fx) =', id (fx)) 3... fx = 10 4 ... печать ('fx =', fx, '/ id (fx) =', id (fx)) 5 ... 6 7 >>> х = 5 8 >>> print ('x =', x, '/ id (x) =', id (x)) 9x = 5 / id (x) = 13578 14 15 >>> печать ('x =', x, '/ id (x) =', id (x)) 16x = 5 / id (x) = 13578 10 11 >>> f (x) 12fx = 5 / id (fx) = 1357
8 13fx = 10 / id (fx) = 1357
8
При первом запуске 8 f () , fx и x оба указывают на один и тот же объект, чей id () равен 1357.После того, как f () выполнит оператор fx = 10 в строке 3, fx указывает на другой объект, чей id () равен 13578 . Связь с исходным объектом в вызывающей среде теряется.
Передача аргумента в Python — это своего рода гибрид между передачей по значению и передачей по ссылке. В функцию передается ссылка на объект, но ссылка передается по значению.
Примечание. Механизм передачи аргументов Python был назван передачей по назначению .Это связано с тем, что имена параметров привязаны к объектам при вводе функции в Python, а присвоение также является процессом привязки имени к объекту. Вы также можете увидеть термины «передача по объекту», «передача по объектной ссылке» или «передача по совместному использованию».
Ключевой вывод здесь заключается в том, что функция Python не может изменить значение аргумента, переназначив соответствующий параметр чему-то другому. Следующий пример демонстрирует это:
>>> >>> def f (x):
... x = 'foo'
...
>>> для i в (
... 40,
... dict (foo = 1, bar = 2),
... {1, 2, 3},
... 'бар',
... ['foo', 'bar', 'baz']):
... f (я)
... печать (я)
...
40
{'foo': 1, 'bar': 2}
{1, 2, 3}
бар
['фу', 'бар', 'баз']
Здесь объекты типа int , dict , set , str и list передаются в f () в качестве аргументов. f () пытается назначить каждый объект строковому 'foo' , но, как вы можете видеть, вернувшись в вызывающую среду, все они не изменились.Как только f () выполнит присвоение x = 'foo' , ссылка будет rebound , и связь с исходным объектом будет потеряна.
Означает ли это, что функция Python вообще никогда не может изменять свои аргументы? На самом деле нет, это не так! Посмотрите, что здесь происходит:
>>> >>> def f (x):
... x [0] = '---'
...
>>> my_list = ['foo', 'bar', 'baz', 'qux']
>>> f (мой_лист)
>>> мой_лист
['---', 'bar', 'baz', 'qux']
В этом случае аргумент f () является списком.Когда вызывается f () , передается ссылка на my_list . Вы уже видели, что f () не может переназначить my_list оптом. Если бы x было назначено чему-то другому, то оно было бы привязано к другому объекту, и соединение с my_list было бы потеряно.
Однако f () может использовать ссылку для внесения изменений в my_list . Здесь f () изменил первый элемент.Вы можете видеть, что после возврата из функции my_list фактически был изменен в вызывающей среде. То же самое относится и к словарю:
>>> def f (x):
... x ['bar'] = 22
...
>>> my_dict = {'foo': 1, 'bar': 2, 'baz': 3}
>>> f (my_dict)
>>> my_dict
{'foo': 1, 'bar': 22, 'baz': 3}
Здесь f () использует x в качестве ссылки для внесения изменений в my_dict .Это изменение отражается в вызывающей среде после возврата f () .
Сводка по передаче аргументов
Аргумент, передаваемый в Python, можно резюмировать следующим образом. Передача неизменяемого объекта , такого как int , str , кортеж или frozenset , в функцию Python действует как передача по значению. Функция не может изменять объект в вызывающей среде.
Передача изменяемого объекта , такого как список , dict или набор , действует в некоторой степени, но не в точности, как передача по ссылке.Функция не может переназначить объект оптом, но она может изменять элементы на месте внутри объекта, и эти изменения будут отражены в вызывающей среде.
Побочные эффекты
Итак, в Python вы можете изменить аргумент из функции, чтобы изменение отражалось в вызывающей среде. Но стоит ли вам это делать? Это пример того, что на жаргоне программирования называется побочным эффектом .
В более общем смысле говорят, что функция Python вызывает побочный эффект, если она каким-либо образом изменяет среду своего вызова.Изменение значения аргумента функции — лишь одна из возможностей.
Примечание: Вы, вероятно, знакомы с побочными эффектами в области здоровья человека, где этот термин обычно относится к непреднамеренным последствиям лекарств. Часто последствия нежелательны, например, рвота или седативный эффект. С другой стороны, побочные эффекты можно использовать намеренно. Например, некоторые лекарства вызывают стимуляцию аппетита, что может быть полезно, даже если это не является основным назначением лекарства.
Концепция аналогична в программировании. Если побочный эффект является хорошо задокументированной частью спецификации функции, и пользователь функции четко знает, когда и как вызывающая среда может быть изменена, тогда все в порядке. Но программист не всегда может должным образом документировать побочные эффекты или даже не подозревать о возникновении побочных эффектов.
Когда они скрыты или неожиданны, побочные эффекты могут привести к программным ошибкам, которые очень трудно отследить.Как правило, их лучше избегать.
Возврат
ЗаявлениеЧто тогда делать функции Python? В конце концов, во многих случаях, если функция не вызывает каких-либо изменений в вызывающей среде, тогда нет никакого смысла в ее вызове. Как функция должна влиять на вызывающего?
Ну, одна из возможностей — использовать возвращаемые значения функции . Оператор return в функции Python служит двум целям:
- Он немедленно завершает функцию и передает управление выполнением обратно вызывающей стороне.
- Он предоставляет механизм, с помощью которого функция может передавать данные обратно вызывающей стороне.
Выход из функции
Внутри функции оператор return вызывает немедленный выход из функции Python и передачу выполнения обратно вызывающей стороне:
>>> def f ():
... печать ('фу')
... печать ('полоса')
... возвращаться
...
>>> f ()
фу
бар
В этом примере оператор return фактически лишний.Функция вернется к вызывающему, когда упадет с конца , то есть после выполнения последнего оператора тела функции. Таким образом, эта функция будет вести себя идентично без оператора return .
Однако, return операторов не обязательно должны быть в конце функции. Они могут появляться в любом месте тела функции и даже несколько раз. Рассмотрим этот пример:
1 >>> def f (x):
2 ... если x <0:
3... возвращаться
4 ... если x> 100:
5 ... вернуться
6 ... печать (x)
7 ...
8
9 >>> f (-3)
10 >>> f (105)
11 >>> f (64)
1264
Первые два вызова f () не вызывают никакого вывода, потому что выполняется инструкция return и функция завершается преждевременно, до того, как будет достигнут оператор print () в строке 6.
Такая парадигма может быть полезна для проверки ошибок в функции. Вы можете проверить несколько условий ошибки в начале функции, с помощью операторов , возвращающих , которые срабатывают в случае возникновения проблемы:
def f ():
если error_cond1:
возвращаться
если error_cond2:
возвращаться
если error_cond3:
возвращаться
<нормальная обработка>
Если ни одно из условий ошибки не обнаружено, функция может продолжить свою обычную обработку.
Возврат данных вызывающему абоненту
Помимо выхода из функции, оператор return также используется для передачи данных обратно вызывающей стороне . Если за оператором возврата внутри функции Python следует выражение, то в вызывающей среде вызов функции оценивается как значение этого выражения:
1 >>> def f ():
2 ... вернуть 'foo'
3 ...
4
5 >>> s = f ()
6 >>> с
7'фу '
Здесь значение выражения f () в строке 5 равно 'foo' , которое впоследствии присваивается переменной s .
Функция может возвращать любой тип объекта . В Python это означает что угодно. В вызывающей среде вызов функции может использоваться синтаксически любым способом, который имеет смысл для типа объекта, который возвращает функция.
Например, в этом коде f () возвращает словарь. Тогда в вызывающей среде выражение f () представляет словарь, а f () ['baz'] является действительной ключевой ссылкой в этот словарь:
>>> def f ():
... return dict (foo = 1, bar = 2, baz = 3)
...
>>> f ()
{'foo': 1, 'bar': 2, 'baz': 3}
>>> f () ['баз']
3
В следующем примере f () возвращает строку, которую можно разрезать, как любую другую строку:
>>> def f ():
... вернуть 'foobar'
...
>>> f () [2: 4]
'ob'
Здесь f () возвращает список, который можно индексировать или разрезать:
>>> def f ():
... return ['foo', 'bar', 'baz', 'qux']
...
>>> f ()
['foo', 'bar', 'baz', 'qux']
>>> f () [2]
'баз'
>>> f () [:: - 1]
['qux', 'baz', 'bar', 'foo']
Если в инструкции return указано несколько выражений, разделенных запятыми, они упаковываются и возвращаются как кортеж:
>>> def f ():
... вернуть 'foo', 'bar', 'baz', 'qux'
...
>>> тип (f ())
<класс 'кортеж'>
>>> t = f ()
>>> т
('foo', 'bar', 'baz', 'qux')
>>> a, b, c, d = f ()
>>> print (f'a = {a}, b = {b}, c = {c}, d = {d} ')
a = foo, b = bar, c = baz, d = qux
Если возвращаемое значение не указано, функция Python возвращает специальное значение Python Нет :
>>> def f ():
... возвращаться
...
>>> print (f ())
Никто
То же самое происходит, если тело функции вообще не содержит оператора return и функция падает с конца:
>>> def g ():
... проходить
...
>>> print (g ())
Никто
Напомним, что Нет. является ложным при оценке в логическом контексте.
Поскольку функции, которые выходят через пустой оператор , возвращают или выпадают из конца, возвращают Нет , вызов такой функции может использоваться в логическом контексте:
>>> def f ():
... возвращаться
...
>>> def g ():
... проходить
...
>>> если f () или g ():
... печать ('да')
... еще:
... печать ('нет')
...
нет
Здесь вызовы как f (), , так и g () являются ложными, поэтому f () или g () также являются ложными, и выполняется предложение else .
Возвращаясь к побочным эффектам
Предположим, вы хотите написать функцию, которая принимает целочисленный аргумент и удваивает его. То есть вы хотите передать целочисленную переменную в функцию, и когда функция вернется, значение переменной в вызывающей среде должно быть вдвое больше, чем было.В Паскале это можно сделать с помощью передачи по ссылке:
1процедура double (var x: integer);
2начать
3 х: = х * 2;
4end;
5
6вар
7 x: целое число;
8
9начало
10 х: = 5;
11 Writeln ('Перед вызовом процедуры:', x);
12 двойных (х);
13 Writeln ('После вызова процедуры:', x);
14 конец.
Выполнение этого кода дает следующий результат, который подтверждает, что double () действительно изменяет x в вызывающей среде:
Перед процедурой вызов: 5
После вызова процедуры: 10
В Python это не сработает.Как вы теперь знаете, целые числа Python неизменяемы, поэтому функция Python не может изменить целочисленный аргумент с помощью побочного эффекта:
>>> >>> def double (x):
... х * = 2
...
>>> х = 5
>>> двойной (х)
>>> х
5
Однако вы можете использовать возвращаемое значение для получения аналогичного эффекта. Просто напишите double () , чтобы он принимал целочисленный аргумент, удваивал его и возвращал удвоенное значение. Затем вызывающий абонент отвечает за присвоение, изменяющее исходное значение:
>>> def double (x):
... вернуть x * 2
...
>>> х = 5
>>> х = двойной (х)
>>> х
10
Возможно, это предпочтительнее модификации с помощью побочного эффекта. Совершенно очевидно, что x изменяется в вызывающей среде, потому что вызывающий делает это сам. В любом случае, это единственный вариант, потому что модификация с помощью побочного эффекта в этом случае не работает.
Тем не менее, даже в тех случаях, когда можно изменить аргумент с помощью побочного эффекта, использование возвращаемого значения может быть более ясным.Предположим, вы хотите удвоить каждый элемент в списке. Поскольку списки изменяемы, вы можете определить функцию Python, которая изменяет список на месте:
>>> >>> def double_list (x):
... я = 0
... пока я >> a = [1, 2, 3, 4, 5]
>>> double_list (а)
>>> а
[2, 4, 6, 8, 10]
В отличие от double () в предыдущем примере, double_list () фактически работает так, как задумано.Если в документации для функции четко указано, что содержимое аргумента списка изменено, это может быть разумной реализацией.
Однако вы также можете написать double_list () , чтобы передать желаемый список обратно по возвращаемому значению и позволить вызывающей стороне выполнить назначение, аналогично тому, как double () был переписан в предыдущем примере:
>>> def double_list (x):
... r = []
... для i в x:
... р.добавить (я * 2)
... вернуть г
...
>>> a = [1, 2, 3, 4, 5]
>>> а = двойной_лист (а)
>>> а
[2, 4, 6, 8, 10]
Любой подход работает одинаково хорошо. Как это часто бывает, это вопрос стиля и личных предпочтений. Побочные эффекты не обязательно являются абсолютным злом, и они имеют свое место, но поскольку практически все может быть возвращено из функции, то же самое обычно можно достичь и с помощью возвращаемых значений.
Списки аргументов переменной длины
В некоторых случаях, когда вы определяете функцию, вы можете не знать заранее, сколько аргументов вы хотите, чтобы она принимала.Предположим, например, что вы хотите написать функцию Python, которая вычисляет среднее нескольких значений. Начать можно примерно так:
>>> >>> def avg (a, b, c):
... return (a + b + c) / 3
...
Все хорошо, если вы хотите усреднить три значения:
Однако, как вы уже видели, когда используются позиционные аргументы, количество переданных аргументов должно соответствовать количеству объявленных параметров. Тогда ясно, что с этой реализацией avg () для любого количества значений, кроме трех, не все в порядке:
>>> ср (1, 2, 3, 4)
Отслеживание (последний вызов последний):
Файл "", строка 1, в
средн. (1, 2, 3, 4)
TypeError: avg () принимает 3 позиционных аргумента, но было дано 4
Вы можете попробовать определить avg () с дополнительными параметрами:
>>> def avg (a, b = 0, c = 0, d = 0, e = 0):
....
....
....
...
Это позволяет указывать переменное количество аргументов. Следующие вызовы, по крайней мере, синтаксически верны:
в среднем (1)
средн. (1, 2)
средн. (1, 2, 3)
средн. (1, 2, 3, 4)
средн. (1, 2, 3, 4, 5)
Но при таком подходе все еще есть несколько проблем. Во-первых, он по-прежнему позволяет использовать до пяти аргументов, а не произвольное число. Что еще хуже, нет способа отличить указанные аргументы от аргументов, которым разрешено использовать по умолчанию.Функция не может узнать, сколько аргументов было передано на самом деле, поэтому она не знает, на что делить:
>>> >>> def avg (a, b = 0, c = 0, d = 0, e = 0):
... return (a + b + c + d + e) / # На что делить ???
...
Очевидно, и этого не пойдет.
Вы можете написать avg () , чтобы получить единственный аргумент списка:
>>> def avg (a):
... всего = 0
... для v в:
... всего + = v
... return total / len (a)
...
>>> avg ([1, 2, 3])
2.0
>>> avg ([1, 2, 3, 4, 5])
3.0
По крайней мере, это работает. Он допускает произвольное количество значений и дает правильный результат. В качестве дополнительного бонуса это работает, когда аргумент также является кортежем:
>>> >>> t = (1, 2, 3, 4, 5)
>>> avg (t)
3.0
Недостатком является то, что дополнительный этап группировки значений в список или кортеж, вероятно, не является тем, чего ожидает пользователь функции, и это не очень элегантно.Всякий раз, когда вы находите код Python, который выглядит неэлегантно, вероятно, есть лучший вариант.
В этом случае действительно есть! Python предоставляет способ передать функции переменное количество аргументов с упаковкой и распаковкой кортежа аргументов с помощью оператора звездочки ( * ).
Упаковка кортежей аргументов
Когда имени параметра в определении функции Python предшествует звездочка ( * ), это указывает на упаковку кортежа аргументов . Все соответствующие аргументы в вызове функции упаковываются в кортеж, на который функция может ссылаться по заданному имени параметра.Вот пример:
>>> def f (* args):
... печать (аргументы)
... print (тип (аргументы), len (аргументы))
... для x в аргументах:
... печать (x)
...
>>> f (1, 2, 3)
(1, 2, 3)
<класс 'кортеж'> 3
1
2
3
>>> f ('foo', 'bar', 'baz', 'qux', 'quux')
('foo', 'bar', 'baz', 'qux', 'quux')
<класс 'кортеж'> 5
фу
бар
баз
qux
quux
В определении f () спецификация параметра * args указывает на упаковку кортежа.При каждом вызове f () аргументы упаковываются в кортеж, на который функция может ссылаться по имени args . Можно использовать любое имя, но аргументов выбирают настолько часто, что это практически стандарт.
Используя упаковку кортежей, вы можете очистить avg () следующим образом:
>>> def avg (* args):
... всего = 0
... для i в аргументах:
... всего + = я
... вернуть total / len (args)
...
>>> avg (1, 2, 3)
2.0
>>> avg (1, 2, 3, 4, 5)
3.0
Еще лучше, вы можете привести его в порядок, заменив цикл на на встроенную функцию Python sum () , которая суммирует числовые значения в любой итерации:
>>> def avg (* args):
... вернуть сумму (аргументы) / len (аргументы)
...
>>> avg (1, 2, 3)
2.0
>>> avg (1, 2, 3, 4, 5)
3.0
Теперь avg () написан лаконично и работает по назначению.
Тем не менее, в зависимости от того, как этот код будет использоваться, может быть, еще есть над чем поработать. Как написано, avg () вызовет исключение TypeError , если какие-либо аргументы не являются числовыми:
>>> avg (1, 'foo', 3)
Отслеживание (последний вызов последний):
Файл "", строка 1, в
Файл "", строка 2, в среднем
TypeError: неподдерживаемые типы операндов для +: 'int' и 'str'
Для максимальной надежности следует добавить код, проверяющий, что аргументы имеют правильный тип.Позже в этой серии руководств вы узнаете, как перехватывать исключения, такие как TypeError , и обрабатывать их соответствующим образом. Вы также можете проверить исключения Python: введение.
Распаковка кортежа аргументов
Аналогичная операция доступна на другой стороне уравнения в вызове функции Python. Когда аргументу в вызове функции предшествует звездочка ( * ), это означает, что аргумент представляет собой кортеж, который должен быть распакован и передан в функцию как отдельные значения:
>>> def f (x, y, z):
... print (f'x = {x} ')
... print (f'y = {y} ')
... печать (f'z = {z} ')
...
>>> f (1, 2, 3)
х = 1
у = 2
г = 3
>>> t = ('фу', 'бар', 'баз')
>>> f (* t)
x = foo
y = бар
z = baz
В этом примере * t в вызове функции указывает, что t - это кортеж, который следует распаковать. Распакованные значения 'foo' , 'bar' и 'baz' назначаются параметрам x , y и z соответственно.
Хотя этот тип распаковки называется распаковкой кортежей , он работает не только с кортежами. Оператор звездочка ( * ) может применяться к любой итерации в вызове функции Python. Например, список или набор тоже можно распаковать:
>>> a = ['foo', 'bar', 'baz']
>>> тип (а)
<список классов>
>>> f (* а)
x = foo
y = бар
z = baz
>>> s = {1, 2, 3}
>>> тип (ы)
<класс 'набор'>
>>> f (* s)
х = 1
у = 2
г = 3
Вы даже можете использовать упаковку и распаковку кортежей одновременно:
>>> >>> def f (* args):
... print (тип (аргументы), аргументы)
...
>>> a = ['foo', 'bar', 'baz', 'qux']
>>> f (* а)
<класс 'кортеж'> ('foo', 'bar', 'baz', 'qux')
Здесь f (* a) указывает, что список a должен быть распакован, а элементы переданы в f () как отдельные значения. Спецификация параметра * args заставляет значения упаковываться обратно в кортеж args .
Упаковка словаря аргументов
Python имеет аналогичный оператор, двойную звездочку ( ** ), который можно использовать с параметрами и аргументами функции Python для указания упаковки и распаковки словаря .Двойная звездочка ( ** ) перед параметром в определении функции Python указывает на то, что соответствующие аргументы, которые, как ожидается, будут парами ключ = значение , должны быть упакованы в словарь:
>>> def f (** kwargs):
... печать (kwargs)
... print (введите (kwargs))
... для ключа val в kwargs.items ():
... print (ключ, '->', val)
...
>>> f (foo = 1, bar = 2, baz = 3)
{'foo': 1, 'bar': 2, 'baz': 3}
<класс 'dict'>
foo -> 1
бар -> 2
баз -> 3
В этом случае аргументы foo = 1 , bar = 2 и baz = 3 упакованы в словарь, на который функция может ссылаться по имени kwargs .Опять же, можно использовать любое имя, но своеобразное kwargs (сокращение от ключевого слова args ) почти стандартно. Вам не обязательно его придерживаться, но если вы это сделаете, то любой, кто знаком с соглашениями о кодировании Python, сразу поймет, что вы имеете в виду.
Распаковка словаря аргументов
Распаковка словаря аргументов аналогична распаковке кортежа аргументов. Когда двойная звездочка ( ** ) предшествует аргументу в вызове функции Python, она указывает, что аргумент является словарем, который должен быть распакован, с полученными элементами, переданными в функцию как аргументы ключевого слова:
>>> def f (a, b, c):
... print (F'a = {a} ')
... печать (F'b = {b} ')
... печать (F'c = {c} ')
...
>>> d = {'a': 'foo', 'b': 25, 'c': 'qux'}
>>> е (** д)
a = foo
б = 25
c = qux
Элементы в словаре d распаковываются и передаются в f () как аргументы ключевого слова. Итак, f (** d) эквивалентно f (a = 'foo', b = 25, c = 'qux') :
>>> f (a = 'foo', b = 25, c = 'qux')
a = foo
б = 25
c = qux
На самом деле, посмотрите это:
>>> >>> f (** dict (a = 'foo', b = 25, c = 'qux'))
a = foo
б = 25
c = qux
Здесь dict (a = 'foo', b = 25, c = 'qux') создает словарь из указанных пар ключ / значение.Затем оператор двойной звездочки ( ** ) распаковывает его и передает ключевые слова в f () .
Собираем все вместе
Подумайте о * args как о позиционном списке аргументов переменной длины, а о ** kwargs как о списке аргументов ключевого слова переменной длины.
Все три - стандартные позиционные параметры, * args и ** kwargs - могут использоваться в одном определении функции Python. Если да, то их следует указывать в таком порядке:
>>> def f (a, b, * args, ** kwargs):
... print (F'a = {a} ')
... печать (F'b = {b} ')
... print (F'args = {args} ')
... печать (F'kwargs = {kwargs} ')
...
>>> f (1, 2, 'foo', 'bar', 'baz', 'qux', x = 100, y = 200, z = 300)
а = 1
b = 2
args = ('foo', 'bar', 'baz', 'qux')
kwargs = {'x': 100, 'y': 200, 'z': 300}
Это обеспечивает столько гибкости, сколько вам может понадобиться в функциональном интерфейсе!
Множественные распаковки в вызове функции Python
Python версии 3.5 представил поддержку дополнительных обобщений распаковки, как описано в PEP 448.Одна вещь, которую позволяют эти улучшения, - это нескольких распаковок за один вызов функции Python:
>>> >>> def f (* args):
... для i в аргументах:
... печать (я)
...
>>> a = [1, 2, 3]
>>> t = (4, 5, 6)
>>> s = {7, 8, 9}
>>> f (* a, * t, * s)
1
2
3
4
5
6
8
9
7
Вы также можете указать несколько распаковок словарей в вызове функции Python:
>>> >>> def f (** kwargs):
... для k, v в kwargs.items ():
... print (k, '->', v)
...
>>> d1 = {'a': 1, 'b': 2}
>>> d2 = {'x': 3, 'y': 4}
>>> f (** d1, ** d2)
а -> 1
б -> 2
х -> 3
у -> 4
Примечание. Это расширение доступно только в Python версии 3.5 или новее. Если вы попробуете это в более ранней версии, то получите исключение SyntaxError .
Кстати, операторы распаковки * и ** не применяются только к переменным, как в примерах выше.Вы также можете использовать их с литералами, которые повторяются:
>>> def f (* args):
... для i в аргументах:
... печать (я)
...
>>> f (* [1, 2, 3], * [4, 5, 6])
1
2
3
4
5
6
>>> def f (** kwargs):
... для k, v в kwargs.items ():
... print (k, '->', v)
...
>>> f (** {'a': 1, 'b': 2}, ** {'x': 3, 'y': 4})
а -> 1
б -> 2
х -> 3
у -> 4
Здесь литеральные списки [1, 2, 3] и [4, 5, 6] указаны для распаковки кортежей, а буквальные словари {'a': 1, 'b': 2} и {'x': 3, 'y': 4} указаны для распаковки словаря.
Аргументы только для ключевых слов
Функцию Python в версии 3.x можно определить так, чтобы она принимала аргументов только для ключевых слов . Это аргументы функции, которые должны быть указаны с помощью ключевого слова. Давайте рассмотрим ситуацию, в которой это может быть полезно.
Предположим, вы хотите написать функцию Python, которая принимает переменное количество строковых аргументов, объединяет их вместе, разделенные точкой ( "." ), и выводит их на консоль. Для начала подойдет что-то вроде этого:
>>> def concat (* args):
... print (f '-> {".". join (args)}')
...
>>> concat ('a', 'b', 'c')
-> a.b.c
>>> concat ('foo', 'bar', 'baz', 'qux').
-> foo.bar.baz.qux
В существующем виде выходной префикс жестко запрограммирован на строку '->' . Что, если вы хотите изменить функцию, чтобы она также принимала это как аргумент, чтобы пользователь мог указать что-то еще? Это одна из возможностей:
>>> def concat (префикс, * args):
... print (f '{префикс} {".".join (аргументы)} ')
...
>>> concat ('//', 'a', 'b', 'c')
//a.b.c
>>> concat ('...', 'foo', 'bar', 'baz', 'qux')
... foo.bar.baz.qux
Это работает так, как рекламируется, но в этом решении есть несколько нежелательных моментов:
Строка префикса
объединяется вместе со строками, которые необходимо объединить. Просто взглянув на вызов функции, неясно, обрабатывается ли первый аргумент иначе, чем остальные. Чтобы узнать это, вам нужно вернуться назад и посмотреть на определение функции.- Префикс
не является обязательным. Его всегда нужно включать, и нет возможности принять значение по умолчанию.
Вы могли подумать, что можете решить вторую проблему, указав параметр со значением по умолчанию, например, например:
>>> >>> def concat (prefix = '->', * args):
... print (f '{префикс} {".". join (args)}')
...
К сожалению, это работает не совсем правильно. Префикс является позиционным параметром , поэтому интерпретатор предполагает, что первый аргумент, указанный в вызове функции, является предполагаемым выходным префиксом.Это означает, что его нельзя пропустить и получить значение по умолчанию:
>>> concat ('a', 'b', 'c')
ab.c
Что, если вы попытаетесь указать префикс в качестве аргумента ключевого слова? Ну, сначала не указывайте:
>>> concat (prefix = '//', 'a', 'b', 'c')
Файл "", строка 1
SyntaxError: позиционный аргумент следует за аргументом ключевого слова
Как вы видели ранее, когда даны оба типа аргументов, все позиционные аргументы должны предшествовать любым аргументам ключевого слова.
Однако вы также не можете указать его последним:
>>> >>> concat ('a', 'b', 'c', prefix = '...')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: concat () получил несколько значений для аргумента prefix
Опять же, префикс является позиционным параметром, поэтому ему назначается первый аргумент, указанный в вызове (в данном случае это 'a' ). Затем, когда он снова указывается в качестве аргумента ключевого слова в конце, Python думает, что он был назначен дважды.
Параметры только для ключевых слов помогают решить эту дилемму. В определении функции укажите * args , чтобы указать переменное количество позиционных аргументов, а затем укажите префикс после этого:
>>> def concat (* args, prefix = '->'):
... print (f '{префикс} {".". join (args)}')
...
В этом случае префикс становится параметром только для ключевого слова. Его значение никогда не будет заполнено позиционным аргументом.Его можно указать только с помощью именованного аргумента ключевого слова:
>>> concat ('a', 'b', 'c', prefix = '...')
... a.b.c
Обратите внимание, что это возможно только в Python 3. В версии 2.x Python указание дополнительных параметров после параметра аргументов переменной * args вызывает ошибку.
, содержащие только ключевое слово, позволяют функции Python принимать переменное количество аргументов, за которыми следует одна или несколько дополнительных опций в качестве аргументов ключевого слова.Если вы хотите изменить concat () , чтобы можно было указать и символ-разделитель, вы можете добавить дополнительный аргумент, состоящий только из ключевых слов:
>>> def concat (* args, prefix = '->', sep = '.'):
... print (f '{префикс} {sep.join (args)}')
...
>>> concat ('a', 'b', 'c')
-> a.b.c
>>> concat ('a', 'b', 'c', prefix = '//')
//a.b.c
>>> concat ('a', 'b', 'c', prefix = '//', sep = '-')
// а-б-в
Если параметру, содержащему только ключевое слово, присвоено значение по умолчанию в определении функции (как в приведенном выше примере), а ключевое слово опущено при вызове функции, то предоставляется значение по умолчанию:
>>> >>> concat ('a', 'b', 'c')
-> а.до н.э
Если, с другой стороны, параметру не присвоено значение по умолчанию, то он становится обязательным, и если его не указать, возникает ошибка:
>>> >>> def concat (* args, префикс):
... print (f '{префикс} {".". join (args)}')
...
>>> concat ('a', 'b', 'c', prefix = '...')
... a.b.c
>>> concat ('a', 'b', 'c')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: в concat () отсутствует 1 обязательный аргумент, содержащий только ключевое слово: 'prefix'
Что, если вы хотите определить функцию Python, которая принимает аргумент, состоящий только из ключевых слов, но не принимает переменное количество позиционных аргументов? Например, следующая функция выполняет указанную операцию с двумя числовыми аргументами:
>>> >>> def oper (x, y, op = '+'):
... если op == '+':
... вернуть x + y
... elif op == '-':
... вернуть x - y
... elif op == '/':
... вернуть x / y
... еще:
... return None
...
>>> опер (3, 4)
7
>>> опер (3, 4, '+')
7
>>> опер (3, 4, '/')
0,75
Если вы хотите сделать op параметром только для ключевых слов, вы можете добавить посторонний параметр аргумента фиктивной переменной и просто игнорировать его:
>>> def oper (x, y, * ignore, op = '+'):
... если op == '+':
... вернуть x + y
... elif op == '-':
... вернуть x - y
... elif op == '/':
... вернуть x / y
... еще:
... return None
...
>>> oper (3, 4, op = '+')
7
>>> oper (3, 4, op = '/')
0,75
Проблема с этим решением состоит в том, что * ignore поглощает любые посторонние позиционные аргументы, которые могут быть включены:
>>> oper (3, 4, «Мне здесь не место»)
7
>>> oper (3, 4, «Мне здесь не место», op = '/')
0.75
В этом примере не должно быть дополнительного аргумента (как объявляет сам аргумент). Вместо того, чтобы тихо добиться успеха, это действительно должно привести к ошибке. То, что это не так, в лучшем случае неопрятно. В худшем случае это может привести к вводящему в заблуждение результату:
Чтобы исправить это, версия 3 позволяет параметру аргумента переменной в определении функции Python быть просто звездочкой ( * ) с опущенным именем:
>>> def oper (x, y, *, op = '+'):
... если op == '+':
... вернуть x + y
... elif op == '-':
... вернуть x - y
... elif op == '/':
... вернуть x / y
... еще:
... return None
...
>>> oper (3, 4, op = '+')
7
>>> oper (3, 4, op = '/')
0,75
>>> oper (3, 4, «Мне здесь не место»)
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: oper () принимает 2 позиционных аргумента, но было дано 3
>>> опер (3, 4, '+')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: oper () принимает 2 позиционных аргумента, но было дано 3
Параметр простого переменного аргумента * указывает, что больше нет позиционных параметров.Это поведение генерирует соответствующие сообщения об ошибках, если указаны дополнительные. Он позволяет следовать параметрам, содержащим только ключевые слова.
Позиционные аргументы
Начиная с Python 3.8, параметры функции также могут быть объявлены только позиционно , что означает, что соответствующие аргументы должны быть предоставлены позиционно и не могут быть указаны с помощью ключевого слова.
Чтобы обозначить некоторые параметры как позиционные, вы указываете косую черту (/) в списке параметров определения функции.Все параметры слева от косой черты (/) должны быть указаны позиционно. Например, в следующем определении функции x и y являются позиционными параметрами, но z можно указать с помощью ключевого слова:
>>> # Это Python 3.8
>>> def f (x, y, /, z):
... print (f'x: {x} ')
... print (f'y: {y} ')
... print (f'z: {z} ')
...
Это означает, что действительны следующие вызовы:
>>> >>> f (1, 2, 3)
х: 1
г: 2
z: 3
>>> f (1, 2, z = 3)
х: 1
г: 2
z: 3
Однако следующий вызов f () недействителен:
>>> f (x = 1, y = 2, z = 3)
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: f () получила некоторые позиционные аргументы, переданные как аргументы ключевого слова:
'х, у'
В одном определении функции могут использоваться позиционные и только ключевые слова:
>>> >>> # Это Python 3.8
>>> def f (x, y, /, z, w, *, a, b):
... print (x, y, z, w, a, b)
...
>>> f (1, 2, z = 3, w = 4, a = 5, b = 6)
1 2 3 4 5 6
>>> f (1, 2, 3, w = 4, a = 5, b = 6)
1 2 3 4 5 6
В этом примере:
-
xиyявляются только позиционными. -
aиb- только ключевые слова. -
zиwмогут быть указаны позиционно или по ключевому слову.
Для получения дополнительной информации о позиционных параметрах см. Основные моменты выпуска Python 3.8.
Строки документации
Когда первая инструкция в теле функции Python является строковым литералом, она называется строкой документации функции . Строка документации используется для предоставления документации для функции. Он может содержать назначение функции, аргументы, которые она принимает, информацию о возвращаемых значениях или любую другую информацию, которая, по вашему мнению, будет полезной.
Ниже приведен пример определения функции со строкой документации:
>>> >>> def avg (* args):
... "" "Возвращает среднее значение списка числовых значений." ""
... вернуть сумму (аргументы) / len (аргументы)
...
Технически, строки документации могут использовать любой из механизмов цитирования Python, но рекомендуется использовать тройные кавычки с использованием символов двойных кавычек ( "" "), как показано выше. Если строка документации умещается в одной строке, то котировки закрытия должны находиться на той же строке, что и котировки открытия.
Многострочные строки документации используются для более объемной документации.Многострочная строка документации должна состоять из итоговой строки, за которой следует пустая строка, за которой следует более подробное описание. Закрывающие котировки должны быть на отдельной строке:
>>> >>> def foo (bar = 0, baz = 1):
... "" "Выполните преобразование foo.
...
... Аргументы ключевого слова:
... bar - величина по оси бара (по умолчанию = 0)
... baz - величина по оси baz (по умолчанию = 1)
... "" "
...
...
Форматирование строки документации и семантические соглашения подробно описаны в PEP 257.
Когда определена строка документации, интерпретатор Python назначает ее специальному атрибуту функции с именем __doc__ . Этот атрибут является одним из набора специализированных идентификаторов в Python, которые иногда называют магическими атрибутами или магическими методами , поскольку они предоставляют специальные языковые функции.
Примечание: Эти атрибуты также упоминаются с помощью атрибутов dunder с красочным псевдонимом и методов dunder. Слово dunder объединяет d из double и под из символа подчеркивания ( _ ).В будущих уроках этой серии вы встретите еще много неприятных атрибутов и методов.
Вы можете получить доступ к строке документации функции с помощью выражения . Строки документации для приведенных выше примеров могут отображаться следующим образом:
>>> print (ср .__ doc__)
Возвращает среднее значение списка числовых значений.
>>> печать (foo .__ doc__)
Выполните преобразование foo.
Аргументы ключевого слова:
bar - величина по оси бара (по умолчанию = 0)
baz - величина по оси baz (по умолчанию = 1)
В интерактивном интерпретаторе Python вы можете ввести help (, чтобы отобразить строку документации для :
>>> справка (средн.)
Справка по функции avg в модуле __main__:
avg (* аргументы)
Возвращает среднее значение списка числовых значений.>>> help (foo)
Справка по функции foo в модуле __main__:
foo (bar = 0, baz = 1)
Выполните преобразование foo.
Аргументы ключевого слова:
bar - величина по оси бара (по умолчанию = 0)
baz - величина по оси baz (по умолчанию = 1)
Считается хорошей практикой кодирования указывать строку документации для каждой определяемой вами функции Python. Дополнительные сведения о строках документации см. В документе «Документирование кода Python: полное руководство».
Аннотации функций Python
Начиная с версии 3.0, Python предоставляет дополнительную функцию для документирования функции, которая называется аннотацией функции . Аннотации позволяют прикреплять метаданные к параметрам функции и возвращаемому значению.
Чтобы добавить аннотацию к параметру функции Python, вставьте двоеточие (: ), за которым следует любое выражение после имени параметра в определении функции. Чтобы добавить аннотацию к возвращаемому значению, добавьте символы -> и любое выражение между закрывающей скобкой списка параметров и двоеточием, завершающим заголовок функции.Вот пример:
>>> def f (a: '', b: '') -> '':
... проходить
...
Аннотация для параметра a - это строка '' , для b строка '' , а для значения, возвращаемого функцией, строка '.
Интерпретатор Python создает словарь из аннотаций и назначает их другому специальному атрибуту dunder функции с именем __annotations__ .Аннотации для функции Python f () , показанные выше, могут отображаться следующим образом:
>>> f .__ annotations__
{'a': '', 'b': '', 'return': ''}
Ключи для параметров - это имена параметров. Ключом для возвращаемого значения является строка 'return' :
>>> f .__ annotations __ ['a']
""
>>> f .__ аннотации __ ['b']
''
>>> е.__annotations __ ['return']
''
Обратите внимание, что аннотации не ограничиваются строковыми значениями. Это может быть любое выражение или объект. Например, вы можете аннотировать объекты типа:
>>> >>> def f (a: int, b: str) -> float:
... print (a, b)
... вернуть (3.5)
...
>>> f (1, 'фу')
1 фу
3.5
>>> f .__ annotations__
{'a': , 'b': , 'return': }
Аннотация может быть даже составным объектом, например списком или словарем, поэтому к параметрам и возвращаемому значению можно прикрепить несколько элементов метаданных:
>>> >>> def area (
... р: {
... 'desc': 'радиус круга',
... 'тип': float
...}) -> \
... {
... 'desc': 'площадь круга',
... 'тип': float
...}:
... return 3.14159 * (r ** 2)
...
>>> площадь (2,5)
19,6349375
>>> area .__ annotations__
{'r': {'desc': 'радиус круга', 'type': },
'return': {'desc': 'область круга', 'type': }}
>>> area .__ annotations __ ['r'] ['desc']
'радиус круга'
>>> Площадь.__annotations __ ['return'] ['type']
<класс 'float'>
В приведенном выше примере аннотация прикреплена к параметру r и к возвращаемому значению. Каждая аннотация представляет собой словарь, содержащий описание строки и объект типа.
Если вы хотите назначить значение по умолчанию для параметра, имеющего аннотацию, то значение по умолчанию идет после аннотации:
>>> >>> def f (a: int = 12, b: str = 'baz') -> float:
... print (a, b)
... вернуть (3.5)
...
>>> f .__ annotations__
{'a': , 'b': , 'return': }
>>> f ()
12 баз
3.5
Для чего нужны аннотации? Откровенно говоря, они почти ничего не делают. Они просто вроде как там. Давайте снова посмотрим на один из приведенных выше примеров, но с небольшими изменениями:
>>> >>> def f (a: int, b: str) -> float:
... print (a, b)
... вернуть 1, 2, 3
...
>>> f ('фу', 2,5)
foo 2.5
(1, 2, 3)
Что здесь происходит? Аннотации для f () указывают, что первый аргумент - int , второй аргумент - str , а возвращаемое значение - float . Но последующий вызов f () нарушает все правила! Аргументы: str и float соответственно, а возвращаемое значение - кортеж. И все же переводчик позволяет всему этому скользить без каких-либо жалоб.
Аннотации не накладывают никаких семантических ограничений на код вообще. Это просто биты метаданных, прикрепленные к параметрам функции Python и возвращаемому значению. Python послушно прячет их в словаре, присваивает словарю атрибут dunder функции __annotations__ , и все. Аннотации являются необязательными и вообще не влияют на выполнение функций Python.
Процитирую Амала в Амаль и ночные посетители : «Какая тогда польза от этого?»
Во-первых, аннотации делают хорошую документацию .Вы, конечно, можете указать ту же информацию в строке документации, но размещение ее непосредственно в определении функции добавляет ясности. Типы аргументов и возвращаемое значение очевидны с первого взгляда для такого заголовка функции:
def f (a: int, b: str) -> float:
Конечно, интерпретатор не обеспечивает соблюдение указанных типов, но, по крайней мере, они понятны для тех, кто читает определение функции.
Deep Dive: принудительная проверка типов
Если бы вы были склонны, вы могли бы добавить код для принудительного применения типов, указанных в аннотациях к функциям.Вот функция, которая проверяет фактический тип каждого аргумента на соответствие тому, что указано в аннотации для соответствующего параметра. Он отображает
>>>Истинно, если они совпадают, иЛожь, если нет:>>> def f (a: int, b: str, c: float): ... импорт осмотреть ... args = inspect.getfullargspec (f) .args ... аннотации = inspect.getfullargspec (f) .annotations ... для x в аргументах: ... print (x, '->', ... 'arg is', type (locals () [x]), ',', ... 'annotation is', annotations [x], ... '/', (type (locals () [x])) is annotations [x]) ... >>> f (1, 'foo', 3.3) a -> arg - это, аннотация - это / True b -> arg - , аннотация - / True c -> arg - , аннотация - / True >>> f ('фу', 4.3, 9) a -> arg - , аннотация - / False b -> arg - , аннотация - / False c -> arg - , аннотация - / False >>> f (1, 'фу', 'бар') a -> arg - это , аннотация - это / True b -> arg - , аннотация - / True c -> arg - , аннотация - / False (Модуль
inspectсодержит функции, которые получают полезную информацию о живых объектах - в данном случае функцияf ().)Функция, определенная наподобие указанной выше, при желании может предпринять какие-то корректирующие действия, когда обнаружит, что переданные аргументы не соответствуют типам, указанным в аннотациях.
Фактически, схема использования аннотаций для выполнения проверки статического типа в Python описана в PEP 484. Доступна бесплатная программа проверки статического типа для Python под названием mypy, основанная на спецификации PEP 484.
Есть еще одно преимущество использования аннотаций.Стандартизированный формат, в котором аннотационная информация хранится в атрибуте __annotations__ , поддается синтаксическому анализу сигнатур функций автоматическими инструментами.
В аннотациях нет ничего особенного. Вы даже можете определить свой собственный без специального синтаксиса, предоставляемого Python. Вот определение функции Python с аннотациями объекта типа, прикрепленными к параметрам и возвращаемому значению:
>>> >>> def f (a: int, b: str) -> float:
... возвращаться
...
>>> f .__ annotations__
{'a': , 'b': , 'return': }
По сути, это та же функция со словарем __annotations__ , созданным вручную:
>>> def f (a, b):
... возвращаться
...
>>> f .__ annotations__ = {'a': int, 'b': str, 'return': float}
>>> f .__ annotations__
{'a': , 'b': , 'return': }
Эффект идентичен в обоих случаях, но первый на первый взгляд визуально более привлекателен и удобочитаем.
Фактически, атрибут __annotations__ не сильно отличается от большинства других атрибутов функции. Например, его можно динамически изменять. Вы можете использовать атрибут возвращаемого значения, чтобы подсчитать, сколько раз функция выполняется:
>>> def f () -> 0:
... f .__ аннотации __ ['return'] + = 1
... print (f "f () был выполнен {f .__ annotations __ ['return']} время (с)")
...
>>> f ()
f () было выполнено 1 раз (а)
>>> f ()
f () было выполнено 2 раза (а)
>>> f ()
f () было выполнено 3 раза (а)
Аннотации функций Python - это не что иное, как словари метаданных.Просто так получилось, что вы можете создать их с помощью удобного синтаксиса, поддерживаемого интерпретатором. Это все, что вы хотите из них сделать.
Заключение
По мере роста приложений становится все более важным модулировать код, разбивая его на более мелкие функции управляемого размера. Надеюсь, теперь у вас есть все необходимые инструменты для этого.
Вы узнали:
- Как создать пользовательскую функцию в Python
- Несколько разных способов передать аргументов функции
- Как можно вернуть данные из функции ее вызывающему
- Как добавить документацию к функциям со строками документации и аннотациями
Следующими в этой серии являются два руководства, которые охватывают поиск и сопоставление с образцом .Вы получите подробный обзор модуля Python под названием re , который содержит функции для поиска и сопоставления с использованием универсального синтаксиса шаблонов, называемого регулярным выражением .
5. Функции - начало программирования на Python для начинающих веб-разработчиков
Человеческие существа весьма ограничены в своих способностях удерживать отдельные части информация в их рабочей памяти. Исследования показывают, что для для большинства людей количество несвязанных кусков составляет около семи.Компьютеры, напротив, без труда управляют тысячами отдельных фрагменты информации, никогда не забывая их и не запутывая.
Чтобы люди (программисты) могли писать сложные программы который может охватывать тысячи строк кода, языки программирования имеют функции которые позволяют программистам использовать возможности abstracton для присвоения имен последовательности инструкции, а затем использовать новые имена без учета подробности инструкций, к которым они относятся.
В этой главе обсуждаются функции, одна из Возможности языка Python, поддерживающие такую абстракцию.
5.1. Определение функций и использование
В контексте программирования функция - это именованная последовательность операторов, выполняющая желаемую операцию. Этот операция указана в определении функции . В Python синтаксис для определение функции:
def НАЗВАНИЕ (СПИСОК ПАРАМЕТРОВ):
ЗАЯВЛЕНИЯ
Вы можете придумать любые имена для создаваемых вами функций, кроме нельзя использовать имя, которое является ключевым словом Python.В списке параметров указано какую информацию, если таковая имеется, вы должны предоставить, чтобы использовать новую функцию.
Внутри функции может быть любое количество операторов, но они должны быть
отступ от def .
Определения функций - это составные операторы , похожие на ветвление и операторы цикла, которые мы видели в условных выражениях и циклах главы, что означает, что они состоят из следующих частей:
Заголовок , который начинается с ключевого слова и заканчивается двоеточием.
Тело , состоящее из одного или нескольких операторов 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
Оператор return без значения после того, как он все еще возвращает значение типа мы раньше не видели:
>>> def mystery (): ... возвращаться ... >>> what_is_it = тайна () >>> what_is_it >>> тип (what_is_it) <класс 'NoneType'> >>> печать (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 , которое присваивается результату result . Нет значений
не отображаются в оболочке Python, если они явно не напечатаны.
Любые операторы в теле функции после оператор возврата обнаруженные никогда не будут выполнены и упоминаются как
мертвый код.
>>> 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, а затем увеличивается каждый раз при выполнении тела цикла. Когда
цикл завершается, count содержит результат - общее количество раз
тело цикла было выполнено, что соответствует количеству цифр.
Первый шаг в инкапсуляции этой логики - заключить ее в функцию:
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 >>>
Обратите внимание на кое-что очень важное.Имя переменной, которую мы передаем как
аргумент ( значение ) не имеет ничего общего с именем параметра ( 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 * я, конец = "")
Распечатать()
Здесь мы использовали функцию 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. Локальные переменные
Вам может быть интересно, как мы можем использовать одну и ту же переменную, i , в обоих print_multiples и print_mult_table .Разве это не вызывает проблем, когда
одна из функций меняет значение переменной?
Ответ - нет, потому что i в print_multiples и i в print_mult_table - это , а не одна и та же переменная.
Переменные, созданные внутри определения функции, являются локальными; вы не можете получить доступ к локальная переменная извне своей домашней функции. Это означает, что вы можете иметь несколько переменных с одинаковыми именами, если они не находятся в одном и том же функция.
Python проверяет все операторы функции - если какой-либо из них присваивает значение переменной, это ключ, который Python использует, чтобы сделать переменную локальная переменная.
Диаграмма стека для этой программы показывает, что две переменные с именами i не та же переменная. Они могут относиться к разным значениям и изменять одно
не влияет на другие.
Значение 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, которые мы видели, можно сгруппировать внутри списков и кортежи разными способами. Списки и кортежи также могут быть вложенными, обеспечивая бесчисленное множество возможностей для организации данных. Организация данных для цель упрощения использования называется структура данных.
Время выборов, и мы помогаем подсчитывать количество голосов по мере их поступления. Голоса, прибывающие из отдельных приходов, участков, муниципалитетов, округов и состояния иногда указываются в виде суммы голосов, а иногда в виде списка промежуточных итогов голосов. Обдумав, как лучше всего хранить счетчики, мы решаем использовать список вложенных номеров , который мы определяем следующим образом:
Вложенный список номеров - это список, элементы которого:
номеров
списки вложенных номеров
Обратите внимание, что термин «вложенный список номеров» используется в собственном определении.Рекурсивные определения такие довольно распространены в математике и Информатика. Они позволяют кратко и эффективно описать рекурсивные структуры данных которые частично состоят из более мелких и простых экземпляров самих себя. В определение не является циклическим, так как в какой-то момент мы достигнем списка, который не иметь списков в качестве элементов.
Теперь предположим, что наша задача - написать функцию, которая суммирует все значения в список вложенных номеров. 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 (список вложенных_числов):
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 (элемент)
если самый большой Doctests включены, чтобы предоставить примеры recursive_max в действии.
Дополнительным поворотом к этой проблеме является поиск числового значения для инициализации наибольший . Мы не можем просто использовать nested_num_list [0] , поскольку это может быть либо
число или список. Чтобы решить эту проблему, мы используем цикл while, который назначает наибольшее значение до первого числового значения независимо от того, насколько глубоко оно вложено.
Каждый из двух приведенных выше примеров имеет базовый вариант , который не приводит к
рекурсивный вызов: случай, когда элемент является числом, а не списком.Без
в базовом случае у вас бесконечная рекурсия , и ваша программа не будет работать.
Python останавливается после достижения максимальной глубины рекурсии и возвращает время выполнения
ошибка.
Запишите следующее в файл с именем infinite_recursion.py :
#
# infinite_recursion.py
#
def recursion_depth (число):
print "Число глубины рекурсии% d." % номер
рекурсия_глубина (число + 1)
рекурсия_глубина (0)
В командной строке unix в том же каталоге, в котором вы сохранили
программа, введите следующее:
python infinite_recursion.ру
После того, как вы увидите, как мелькают сообщения, вы увидите конец
длинная трассировка, которая заканчивается следующим образом:
...
Файл infinite_recursion.py, строка 3, в 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
Кроме:
вернуть ложь
Вы можете использовать несколько блоков , кроме , для обработки различных типов исключений.
(см. Ошибки и исключения
урок от Python Tutorial от создателя Python Гвидо ван Россума для более полного обсуждения
исключения).
Если ваша программа обнаруживает состояние ошибки, вы можете заставить ее поднять исключение. Вот пример, который получает ввод от пользователя и проверяет, что
число неотрицательно.
#
# learn_exceptions.py
#
def get_age ():
age = input ('Пожалуйста, введите свой возраст:')
если возраст <0:
Raise ValueError, "% s не является допустимым возрастом"% age
возраст возвращения
Оператор подъема принимает два аргумента: тип исключения и конкретный
информация об ошибке. 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." % номер
пытаться:
рекурсия_глубина (число + 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. Глоссарий
- аргумент
Значение, передаваемое функции при ее вызове. Это значение
присваивается соответствующему параметру в функции.
- поток выполнения
Порядок, в котором операторы выполняются во время выполнения программы.
- кадр
Поле на диаграмме стека, представляющее вызов функции. Это содержит
локальные переменные и параметры функции.
- function
Именованная последовательность операторов, выполняющая некоторые полезные операции.
Функции могут принимать или не принимать параметры, а также могут создавать или не создавать
результат.
- вызов функции
Оператор, выполняющий функцию.Он состоит из названия
функция, за которой следует список аргументов, заключенный в круглые скобки.
- композиция функций
Использование выходных данных одного вызова функции в качестве входных данных для другого.
- определение функции
Оператор, который создает новую функцию, указывая ее имя,
параметры и операторы, которые он выполняет.
Первая часть составного отчета. Заголовки начинаются с ключевого слова и
заканчиваться двоеточием (:)
- локальная переменная
Переменная, определенная внутри функции.Можно использовать только локальную переменную
внутри его функции.
-
Нет Единственное значение . Нет часто используется для
представляют собой отсутствие значения. Он также возвращается с помощью возврата оператор без аргументов или функция, которая достигает конца своего
body без попадания в строку return , содержащую значение.
- параметр
Имя, используемое внутри функции для ссылки на значение, переданное как
аргумент.
- диаграмма стека
Графическое представление стека функций, их переменных,
и ценности, к которым они относятся.
- трассировка
Список выполняемых функций, распечатываемый при выполнении
возникает ошибка. Отслеживание также обычно называют трассировка стека , поскольку он перечисляет функции в том порядке, в котором они
хранятся в
стек времени выполнения.
Определение и вызов функций в Python (def, return)
В этой статье описывается, как определять и вызывать (выполнять) функции в Python.
- Основы определения и вызова функций в Python
- Позиционные аргументы / аргументы ключевого слова
- Позиционные аргументы
- Аргумент ключевого слова
- Позиционный аргумент / только ключевое слово
- Аргумент по умолчанию
- Аргумент переменной длины
-
* args : получение нескольких аргументов в виде кортежа -
** kwargs : Получение нескольких аргументов ключевого слова в виде словаря
- Расширить списки и словари (Распаковка)
- Возвращаемое значение
- Основы возврата
- Функция возврата
Нет - Укажите несколько возвращаемых значений
Лямбда-выражения, используемые для создания анонимных функций, см. В следующей статье.
Официальная документация для определения функции:
Основы определения и вызова функций в Python
В Python функции определяются с помощью операторов def . Параметры указываются в круглых скобках () , а возвращаемое значение указывается с return .
def имя_функции (параметр1, параметр2 ...):
сделай что-нибудь
вернуть return_value
При вызове определенной функции напишите следующее:
имя_функции (аргумент1, аргумент2...)
Пример:
def add (a, b):
х = а + б
вернуть х
х = добавить (3, 4)
печать (х)
# 7
Параметры и возвращаемые значения return могут быть опущены, если в них нет необходимости.
def привет ():
print ('Привет')
Привет()
# Привет
Подробности об аргументах и возвращаемых значениях описаны ниже.
Обратите внимание, что хотя рекомендуется оставить две строки до и после блока def в стандарте кодирования Python PEP8, в примере кода для удобства оставлена только одна строка.
Позиционные / ключевые аргументы
Параметры определяются путем разделения их запятыми в круглых скобках имя_функции () . Пример - простая функция, которая выводит аргументы как есть, используя f-строку.
def func (a, b, c):
print (f'a = {a}, b = {b}, c = {c} ')
Позиционный аргумент
При звонке укажите значения в определенном порядке.
func (1, 10, 100)
# a = 1, b = 10, c = 100
Если количество значений, указанных при вызове функции, не совпадает с количеством параметров при определении функции, возникает ошибка TypeError .
# func (1)
# TypeError: func () отсутствует 2 обязательных позиционных аргумента: 'b' и 'c'
# func (1, 10, 100, 1000)
# TypeError: func () принимает 3 позиционных аргумента, но было дано 4
Аргумент ключевого слова
При вызове функции можно указать значение как имя_параметра = значение . В этом случае их можно указывать в любом порядке.
func (b = 10, c = 100, a = 1)
# a = 1, b = 10, c = 100
Нет необходимости указывать все аргументы ключевыми словами, но их можно указать ключевыми словами после указания позиции.Однако все аргументы после аргументов, указанных в ключевых словах, должны быть указаны ключевыми словами.
func (1, c = 100, b = 10)
# a = 1, b = 10, c = 100
# func (a = 1, 10, 100)
# SyntaxError: позиционный аргумент следует за аргументом ключевого слова
Позиционный аргумент / только ключевое слово
Позиционный аргумент (Python 3.8 или новее)
Если / используется в качестве параметра при определении функции, параметр до / определяется как только позиционный.
Позиционные параметры не могут передаваться по ключевому слову. Параметры, следующие за /, можно передавать по ключевому слову.
def func_pos_only (a, b, /, c):
print (f'a = {a}, b = {b}, c = {c} ')
# func_pos_only (a = 1, b = 10, c = 100)
# TypeError: func_pos_only () получила некоторые позиционные аргументы, переданные как аргументы ключевого слова: 'a, b'
func_pos_only (1, 10, 100)
# a = 1, b = 10, c = 100
func_pos_only (1, 10, c = 100)
# a = 1, b = 10, c = 100
Если вы определяете функцию с / в конце, например, func (a, b, c, /) , все параметры будут только позиционными.
Параметр только позиции с использованием / введен в Python 3.8 и недоступен в более ранних версиях.
Аргумент только ключевое слово
Если * используется в качестве параметра при определении функции, параметр после * определяется как только ключевое слово.
Параметры, зависящие от ключевого слова, должны быть указаны с помощью ключевого слова. Параметры перед * могут быть указаны по позиции или ключевому слову.
def func_kw_only (a, b, *, c):
print (f'a = {a}, b = {b}, c = {c} ')
# func_kw_only (1, 10, 100)
# TypeError: func_kw_only () принимает 2 позиционных аргумента, но было дано 3
func_kw_only (1, 10, c = 100)
# a = 1, b = 10, c = 100
func_kw_only (1, c = 100, b = 10)
# a = 1, b = 10, c = 100
Если вы определяете функцию с * в начале, например, func (*, a, b, c) , все параметры являются только ключевыми словами.
Позиционный аргумент и аргумент только ключевое слово
Можно использовать / и * одновременно. Параметры до / предназначены только для позиции, а параметры после * - только для ключевого слова. Параметры от / до * могут быть позиционными или ключевыми.
def func_pos_kw_only (a, /, b, *, c):
print (f'a = {a}, b = {b}, c = {c} ')
# func_pos_kw_only (1, 10, 100)
# TypeError: func_pos_kw_only () принимает 2 позиционных аргумента, но было дано 3
# func_pos_kw_only (a = 1, b = 10, c = 100)
# TypeError: func_pos_kw_only () получила некоторые позиционные аргументы, переданные как аргументы ключевого слова: 'a'
func_pos_kw_only (1, 10, c = 100)
# a = 1, b = 10, c = 100
func_pos_kw_only (1, c = 100, b = 10)
# a = 1, b = 10, c = 100
Вы не можете использовать / до * .
# def func_pos_kw_only (a, *, b, /, c):
# print (f'a = {a}, b = {b}, c = {c} ')
# SyntaxError: недопустимый синтаксис
Аргумент по умолчанию
Значение параметра по умолчанию может быть установлено путем определения имя_параметра = значение по умолчанию при определении функции.
Если установлено значение по умолчанию, указание аргумента может быть опущено при вызове функции. Конечно, если вы укажете другое значение, оно будет использоваться.
def func_default (a, b, c = 100):
print (f'a = {a}, b = {b}, c = {c} ')
func_default (1, 10)
# a = 1, b = 10, c = 100
func_default (1, 10, 200)
# a = 1, b = 10, c = 200
Размещение параметра по умолчанию перед обычным параметром (параметры без значения по умолчанию) при определении функции вызывает SyntaxError .
# def func_default (a = 1, b, c = 100):
# print (f'a = {a}, b = {b}, c = {c} ')
# SyntaxError: аргумент не по умолчанию следует за аргументом по умолчанию
Обратите внимание, что если вы используете список или словарь в качестве значений по умолчанию, всегда будут использоваться те же объекты, что и при вызове функции. Подробнее см. В следующей статье.
Аргумент переменной длины
Если вы добавите * и ** к именам параметров при определении функции, он станет параметром переменной длины, и вы можете указать любое количество аргументов при вызове функции.
По соглашению часто используются имена * args и ** kwargs , но пока * и ** озаглавлены, проблем с другими именами нет.
* args: получить несколько аргументов в виде кортежа
Если вы поместите * в начало имени, несколько аргументов будут получены в виде кортежа.
def func_args (* args):
печать (аргументы)
func_args (1, 10)
# (1, 10)
func_args (1, 10, 100, 1000)
# (1, 10, 100, 1000)
** kwargs: Получение нескольких аргументов ключевого слова в виде словаря
Если вы поместите ** в начало имени, несколько аргументов ключевого слова будут получены в виде словаря ( dict ).
def func_kwargs (** kwargs):
печать (kwargs)
func_kwargs (a = 1, b = 10)
# {'a': 1, 'b': 10}
func_kwargs (c = 1, b = 10, d = 1000, a = 100)
# {'c': 1, 'b': 10, 'd': 1000, 'a': 100}
Будьте осторожны с порядком при объединении с позиционными аргументами или с * args и ** kwargs . Подробнее см. В следующей статье.
Расширить списки и словари (Распаковка)
Распаковать списки или кортежи
Если вы добавляете * в список или кортеж при вызове функции, элементы раскрываются и указываются в порядке позиционных аргументов. TypeError произойдет, если количество элементов и количество аргументов не совпадают.
def func (a, b, c):
print (f'a = {a}, b = {b}, c = {c} ')
l = [1, 10, 100]
func (* l)
# a = 1, b = 10, c = 100
l = [1, 10]
# func (* l)
# TypeError: func () отсутствует 1 обязательный позиционный аргумент: 'c'
Распаковать словари
Если вы добавляете ** в словарь при вызове функции, ключ элемента раскрывается как имя аргумента, а значение раскрывается как значение аргумента и указывается как аргументы ключевого слова.Если нет ключа, который соответствует имени аргумента, или если есть ключ, который не соответствует имени аргумента, произойдет ошибка TypeError .
d = {'a': 1, 'b': 10, 'c': 100}
func (** d)
# a = 1, b = 10, c = 100
d = {'a': 1, 'b': 10, 'x': 100}
# func (** d)
# TypeError: func () получила неожиданный аргумент ключевого слова 'x'
См. В следующей статье примечания по объединению аргументов по умолчанию или аргументов переменной длины.
Возвращаемое значение
Основы возврата
Возвращаемое значение функции определяется оператором return .
def func_return (a, b):
вернуть a + b
х = func_return (3, 4)
печать (х)
# 7
печать (введите (x))
# <класс 'int'>
Тип возвращаемого значения зависит от типа аргумента и обработки, выполняемой функцией.
x = func_return (0,3; 0,4)
печать (х)
# 0.7
печать (введите (x))
# <класс 'float'>
Функция возврата Нет
return не является обязательным в функции и может быть опущен, если нет необходимости возвращать значение.
Функция, которая пропускает return , возвращает None . В следующем примере используется этап , проход , поскольку ошибка возникает, если в блоке def ничего не записано.
def func_none ():
# сделай что-нибудь
проходить
x = func_none ()
печать (х)
# Никто
Если вы опустите значение после , верните , Нет. не будет возвращено.
def func_none2 ():
возвращаться
x = func_none2 ()
печать (х)
# Никто
Конечно, вы можете явно написать return None .
def func_none3 ():
return None
x = func_none3 ()
печать (х)
# Никто
Укажите несколько возвращаемых значений
Указание нескольких значений, разделенных запятыми, после return вернет кортеж.
def func_return_multi (a, b):
вернуть a + b, a * b, a / b
х = func_return_multi (3, 4)
печать (х)
# (7, 12, 0,75)
печать (введите (x))
# <класс 'кортеж'>
Можно распаковать каждое значение и присвоить его каждой переменной.
x, y, z = func_return_multi (3, 4)
печать (х)
# 7
печать (у)
# 12
печать (z)
# 0,75
Функции в Python - GeeksforGeeks
Функция в Python - это совокупность связанных операторов, предназначенных для выполнения вычислительной, логической или оценочной задачи. Идея состоит в том, чтобы объединить некоторые часто или многократно выполняемые задачи и создать функцию, чтобы вместо того, чтобы писать один и тот же код снова и снова для разных входов, мы могли вызывать функцию, чтобы повторно использовать код, содержащийся в ней, снова и снова.
Функции могут быть как встроенными, так и определяемыми пользователем. Это помогает программе быть краткой, неповторяющейся и организованной.
Синтаксис:
def имя_функции (параметры):
"" "строка документации" ""
оператор (ы) Пример:
Python
def evenOdd (x):
if 902 902 = = 0 ):
печать "четный"
еще :
evenOdd ( 2 )
evenOdd ( 3 )
Docstring
Строка документа или короткая функция в документе называется короткой строкой.Это используется для описания функциональности функции. Использование строки документации в функциях необязательно, но считается хорошей практикой.
Приведенный ниже синтаксис может использоваться для распечатки строки документации функции:
Синтаксис: print (имя_функции .__ doc__)
Пример:
Python3
def 902 ):
«Привет, выродки!»
печать (say_Hi.__doc__)
Выход:
Здравствуйте! выродки!
Оператор return
Оператор return используется для выхода из функции и возврата к вызывающей функции и возврата указанного значения или элемента данных вызывающей стороне.
Синтаксис: return [список_выражений]
Оператор return может состоять из переменной, выражения или константы, которая возвращается в конце выполнения функции.Если ничего из вышеперечисленного не присутствует с оператором return, возвращается объект None.
Пример:
Python3
def square_value (num):
902 902 902
печать (квадратное_значение ( 2 ))
печать (квадратное_значение ( - - 902) Выход:
4
16
Передавать по ссылке или передавать по значению?
Важно отметить, что в Python каждое имя переменной является ссылкой.Когда мы передаем переменную функции, создается новая ссылка на объект. Передача параметров в Python аналогична передаче ссылок в Java.
Пример:
Python
def myFun (x):
x [ 0 ]
8 8 8 8 8 = lst = [ 10 , 11 , 12 , 13 , 14 901 902 (lst)
print (lst)
Вывод [20, 11, 12, 13, 14, 15]
Когда мы передаем ссылку и меняем полученную ссылку на что-то в противном случае связь между переданным и принятым параметром разрывается.Например, рассмотрим приведенную ниже программу.
Python
def myFun (x):
902 902 , 30 , 40 ] lst = [ 10 , , 11 12 11 12 902 , 14 , 15 ]
myFun (lst)
печать (lst)
7, , Выход 13, 14, 15]
Другой пример, демонстрирующий, что ссылка на ссылку не работает, если мы присваиваем новое значение (внутри функции).
Python
def myFun (x):
902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 902 x = 10
myFun (x)
print (x)
Exercise: Попробуйте угадать следующий код.
Python
def swap (x, y):
темп. y = темп
x = 2
y = 3
печать (x) print (y)
Аргументы по умолчанию: Аргумент по умолчанию - это параметр, который принимает значение по умолчанию, если значение не указано в вызове функции для этого аргумента.В следующем примере показаны аргументы по умолчанию.
Python
def myFun (x, y = 50 ):
(печать) , x)
печать ( «y:» , y)
myFun ( 10
('x:'; 10)
('y:', 50) Как и аргументы C ++ по умолчанию, любое количество аргументов в функции может иметь значение по умолчанию.Но если у нас есть аргумент по умолчанию, все аргументы справа также должны иметь значения по умолчанию.
Аргументы ключевого слова: Идея состоит в том, чтобы позволить вызывающей стороне указывать имя аргумента со значениями, чтобы вызывающей стороне не нужно было запоминать порядок параметров.
Python
def студент (имя, фамилия):
печать (имя, фамилия)
4 имя 2 902 902 902 902 902 имя студент «Компьютерщики» , фамилия = «Практика» )
студент (фамилия = «Практика» , имя = 21414 13)
Выход («Гики», «Практика»)
(«Гики», «Практика»)
Аргументы переменной длины:
У нас может быть как обычное, так и ключевое слово, переменное количество аргументов.Пожалуйста, смотрите это для деталей.
Пример 1:
Python
def myFun ( * argv):
argv : print (arg)
myFun ( 'Hello' , 'Добро пожаловать' , GeeksforGeeks ' )
Выход Привет
Добро пожаловать
к
GeeksforGeeks
Пример 2:
Python3
def myFun ( * * 902 902 902 902 902 902 902 902 902 902 902 в кварг.items ():
print ( "% s ==% s" % (ключ, значение))
myFun (первый ) "Компьютерщики" , середина = "для" , последний = "Компьютерщики" )
Выход первый == Гики
mid == для
last == Компьютерщики
В Python анонимная функция означает, что функция не имеет имени.Как мы уже знаем, ключевое слово def используется для определения обычных функций, а ключевое слово lambda используется для создания анонимных функций. Пожалуйста, смотрите это для деталей.
Python3
def куб (x): возврат x * x * *
лямбда x: x * x * x печать (куб ( 7 ) (
7 ))
Ссылки: Внимание компьютерщик! Укрепите свои основы с помощью курса Python Programming Foundation и изучите основы.
Для начала подготовьтесь к собеседованию. Расширьте свои концепции структур данных с помощью курса Python DS .
Учебное пособие по основам Python: функции
Функция в Python определяется с помощью ключевого слова def . Функции не имеют объявленных возвращаемых типов. Функция без явного return возвращает None . В случае отсутствия аргументов и возвращаемого значения определение очень простое.
Вызов функции выполняется с помощью оператора вызова () после имени функции.
>>> def hello_function ():
... print 'Hello World, это я. Функция.
...
>>> hello_function ()
Привет, мир, это я. Функция.
Аргументы функции определены в операторе def . Как и все другие переменные в Python, не существует явного типа, связанного с аргументами функции. Этот факт важно учитывать, делая предположения о типах данных, которые получит ваша функция.
Аргументы функции могут быть дополнительно определены со значением по умолчанию. Значение по умолчанию будет присвоено в том случае, если аргумент отсутствует в вызове функции. Все аргументы без значений по умолчанию должны быть перечислены перед аргументами со значениями по умолчанию в определении функции.
Любой аргумент может быть передан неявно по позиции или явно по имени, независимо от того, определено ли для него значение по умолчанию.
>>> def record_score (name, score = 0):
... выведите '% s набрал% s'% (имя, оценка)
...
>>> record_score ('Джилл', 4)
Джилл забила 4
>>> record_score ('Джек')
Джек забил 0
>>> record_score (оценка = 3, имя = 'Ведро')
Ведро набрано 3
>>> record_score (2)
2 голов 0 голов
>>> record_score (оценка = 2)
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: record_score () принимает как минимум 1 аргумент без ключевого слова (задано 0)
>>> record_score ()
Отслеживание (последний вызов последний):
Файл "", строка 1, в
TypeError: record_score () принимает по крайней мере 1 аргумент (задано 0) Примечание Внимательно посмотрите на приведенный выше пример.Существует асимметрия в использовании знака = для определения и передачи аргументов, что может сбить с толку новичков. Аргумент со значением по умолчанию может быть передан с использованием только позиции, а аргумент без значения по умолчанию может быть передан с использованием keword.
7.3. Изменяемые аргументы и привязка значений по умолчанию
При определении аргументов по умолчанию будьте особенно осторожны с изменяемыми типами данных. Экземпляр значения по умолчанию привязывается во время определения функции.Следовательно, существует единственный экземпляр изменяемого объекта, который будет использоваться во всех вызовах функции.
>>> def add_items (new_items, base_items = []):
... для элемента в new_items:
... base_items.append (элемент)
... вернуть base_items
...
>>> add_items ((1, 2, 3))
[1, 2, 3]
>>> add_items ((1, 2, 3))
[1, 2, 3, 1, 2, 3]
В результате лучше всего использовать значение по умолчанию Нет в качестве флага, чтобы обозначить отсутствие аргумента и обработать регистр внутри тела функции.
>>> def add_items (new_items, base_items = None):
... если base_items равно None:
... base_items = []
... для элемента в new_items:
... base_items.append (элемент)
... вернуть base_items
...
>>> add_items ((1, 2, 3))
[1, 2, 3]
>>> add_items ((1, 2, 3))
[1, 2, 3]
7,4. Принятие переменных аргументов
Помимо именованных аргументов, функции могут принимать два специальных набора аргументов.
Первый - это именованный кортеж переменной длины, состоящий из любых дополнительных позиционных аргументов, полученных функцией. Этот специальный аргумент обозначается префиксом одной звездочки ( * ).
Второй - словарь переменной длины, содержащий все аргументы ключевого слова, переданные функции, которые не были явно определены как часть аргументов функции. Этот аргумент обозначается двумя звездочками ( ** ) перед ним.
Называть эти два аргумента args и kwargs, соответственно, не обязательно, но обычно и поэтому настоятельно рекомендуется.
Использование этих двух аргументов проиллюстрировано в следующем наборе примеров.
>>> def variable_function (* args, ** kwargs):
... напечатать 'args:', args
... выведите 'kwargs:', kwargs
...
>>> переменная_функция ('простой')
args: ('простой',)
kwargs: {}
>>> функция_переменной (тип = 'Сложный')
аргументы: ()
kwargs: {'тип': 'Сложный'}
>>> def mixed_function (a, b, c = None, * args, ** kwargs):
... print '(a, b, c):', (a, b, c)
... напечатать 'args:', args
... выведите 'kwargs:', kwargs
...
>>> смешанная_функция (1, 2, 3, 4, 5, d = 10, e = 20)
(а, б, в): (1, 2, 3)
аргументы: (4, 5)
kwargs: {'e': 20, 'd': 10} 7.5. Распаковка списков аргументов
Также можно создавать списки аргументов (позиционные или ключевые) и передавать их в функцию.
Для позиционных аргументов вставьте их в кортеж / список и поставьте перед вызовом функции звездочку ( * ).
В качестве аргументов ключевого слова используйте словарь и поставьте перед ним две звездочки ( ** ).
>>> def printer (a, b, c = 0, d = None):
... print 'a: {0}, b: {1}, c: {2}, d: {3}'. format (a, b, c, d)
...
>>> принтер (2, 3, 4, 5)
а: 2, б: 3, в: 4, г: 5
>>> заказанный_аргс = (5, 6)
>>> keyword_args = {'c': 7, 'd': 8}
>>> принтер (* заказанные_арги, ** ключевые_арги)
a: 5, b: 6, c: 7, d: 8 Примечание В приведенном выше примере показана еще одна потенциально сбивающая с толку асимметрия в Python.Вы можете передавать аргументы, используя обычный стиль, в функцию, определенную с использованием переменных аргументов, и вы можете передавать распакованные списки переменных аргументов в функцию, определенную без переменных аргументов.
Каждая оценка функции создает локальное пространство имен, которым можно управлять на любом уровне внутри функции.
Оставить комментарий

items():
print("The value of {} is {}".format(key, value))
print_values(my_name="Sammy", your_name="Casey")
sub('[!#?]', '', value)
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']
states = list(map(remove_punctuation, states))
states = list(map(str.strip, states))
states = list(map(str.title, states))
print(states)
casefold(), colors))
print(normalized_colors)
7560'
SyntaxError: invalid syntax
title() + " " + last.title() -> str
File "<stdin>", line 1
lambda first: str, last: str: first.title() + " " + last.title() -> str
SyntaxError: invalid syntax
Обычно это выражается синтаксисом @decorator с префиксом функции. Вот пример:
В отличие от этого, в строке 18 сразу же включается лямбда-функция и встраивается в вызов метода trace(), декоратора. Когда вы выполняете код выше, вы получаете следующее:
В сущности, замыкания определяют среду, в которой они работают, и поэтому могут вызываться из любого места. Более простое определение замыкания это когда функции более низшего порядка имеют доступ к переменным функции более высшего порядка.
В строке 5 inner_func() захватывает x и y, потому что он имеет доступ к своей области видимости, так что при вызове замыкания он может работать с двумя свободными переменными x и y.
.. funcs.append(lambda n=n: print(n))
5 ...
6 >>> for f in funcs:
7 ... f()
8 ...
9 one
10 two
11 three
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)
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.
Следует избегать использования этого типа обходного пути. Если вы сталкиваетесь с этим типом кода, вам следует рассмотреть возможность рефакторинга кода для использования обычной функции.
В разделе Альтернативы лямбда-выражениям вы научитесь заменять map() и лямбда-выражения на списки или выражения-генераторы. Это значительно улучшит читабельность кода.
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'
NET Windows Forms с IronPython, используют лямбда-функции для отображения действий в ответ на события пользовательского интерфейса.
Вот пример:
monkeypatch позволяет вам контролировать область переопределения. В приведенном выше примере при вызове secretts.token_hex() в последующих тестах без использования monkey patching будет выполняться обычная реализация этой функции.
capitalize(), ['cat', 'dog', 'cow']))
['Cat', 'Dog', 'Cow']
Что касается map() и filter(), его первые два аргумента являются соответственно функцией и итерируемым списком. Он также может принимать инициализатор в качестве третьего аргумента, который используется в качестве начального значения результирующего аккумулятора. Для каждого итерируемого элемента reduce() применяет функцию и накапливает результат, который возвращается, когда итерация исчерпана.
Определяются с помощью зарезервированного слова def. В Python к функциям можно обращаться неограниченное количество раз из любой части сценария.
То есть когда интерпретатор приступает к реализации, он создает новый объект и связывает его с именем, указанным после def. Чтобы хранить данные к функциям можно прикреплять различные атрибуты.
В интерпретаторе Python команда print() вернула переменные x и y из глобальной области видимости.