Введение
Определение
Вот пример простой функции:
def compute_surface(radius):
from math import pi
return pi * radius * radius
Для определения функции нужно всего лишь написать ключевое слово def
перед ее именем, а после — поставить двоеточие. Следом идет блок инструкций.
Последняя строка в блоке инструкций может начинаться с return
, если нужно вернуть какое-то значение. Если инструкции return
нет, тогда по умолчанию функция будет возвращать объект None
. Как в этом примере:
i = 0
def increment():
global i
i += 1
Функция инкрементирует глобальную переменную i
и возвращает None
(по умолчанию).
Вызовы
Для вызова функции, которая возвращает переменную, нужно ввести:
surface = compute_surface(1.)
Для вызова функции, которая ничего не возвращает:
increment()
Еще
Функцию можно записать в одну строку, если блок инструкций представляет собой простое выражение:
def sum(a, b): return a + b
Функции могут быть вложенными:
def func1(a, b):
def inner_func(x):
return x*x*x
return inner_func(a) + inner_func(b)
Функции — это объекты, поэтому их можно присваивать переменным.
Инструкция return
Возврат простого значения
Аргументы можно использовать для изменения ввода и таким образом получать вывод функции. Но куда удобнее использовать инструкцию return
, примеры которой уже встречались ранее. Если ее не написать, функция вернет значение None
.
Возврат нескольких значений
Пока что функция возвращала только одно значение или не возвращала ничего (объект None). А как насчет нескольких значений? Этого можно добиться с помощью массива. Технически, это все еще один объект. Например:
def stats(data):
"""данные должны быть списком"""
_sum = sum(data) # обратите внимание на подчеркивание, чтобы избежать переименования встроенной функции sum
mean = _sum / float(len(data)) # обратите внимание на использование функции float, чтобы избежать деления на целое число
variance = sum([(x-mean)**2/len(data) for x in data])
return mean,variance # возвращаем x,y — кортеж!
m, v = stats([1, 2, 1])
Аргументы и параметры
В функции можно использовать неограниченное количество параметров, но число аргументов должно точно соответствовать параметрам. Эти параметры представляют собой позиционные аргументы. Также Python предоставляет возможность определять значения по умолчанию, которые можно задавать с помощью аргументов-ключевых слов.
Параметр — это имя в списке параметров в первой строке определения функции. Он получает свое значение при вызове. Аргумент — это реальное значение или ссылка на него, переданное функции при вызове. В этой функции:
def sum(x, y):
return x + y
x и y — это параметры, а в этой:
sum(1, 2)
1 и 2 — аргументы.
При определении функции параметры со значениями по умолчанию нужно указывать до позиционных аргументов:
def compute_surface(radius, pi=3.14159):
return pi * radius * radius
Если использовать необязательный параметр, тогда все, что указаны справа, должны быть параметрами по умолчанию.
Выходит, что в следующем примере допущена ошибка:
def compute_surface(radius=1, pi):
return pi * radius * radius
Для вызовов это работает похожим образом. Сначала нужно указывать все позиционные аргументы, а только потом необязательные:
S = compute_surface(10, pi=3.14)
На самом деле, следующий вызов корректен (можно конкретно указывать имя позиционного аргумента), но этот способ не пользуется популярностью:
S = compute_surface(radius=10, pi=3.14)
А этот вызов некорректен:
S = compute_surface(pi=3.14, 10)
При вызове функции с аргументами по умолчанию можно указать один или несколько, и порядок не будет иметь значения:
def compute_surface2(radius=1, pi=3.14159):
return pi * radius * radius
S = compute_surface2(radius=1, pi=3.14)
S = compute_surface2(pi=3.14, radius=10.)
S = compute_surface2(radius=10.)
Можно не указывать ключевые слова, но тогда порядок имеет значение. Он должен соответствовать порядку параметров в определении:
S = compute_surface2(10., 3.14)
S = compute_surface2(10.)
Если ключевые слова не используются, тогда нужно указывать все аргументы:
def f(a=1,b=2, c=3):
return a + b + c
Второй аргумент можно пропустить:
f(1,,3)
Чтобы обойти эту проблему, можно использовать словарь:
params = {'a':10, 'b':20}
S = f(**params)
Значение по умолчанию оценивается и сохраняется только один раз при определении функции (не при вызове). Следовательно, если значение по умолчанию — это изменяемый объект, например, список или словарь, он будет меняться каждый раз при вызове функции. Чтобы избежать такого поведения, инициализацию нужно проводить внутри функции или использовать неизменяемый объект:
def inplace(x, mutable=[]):
mutable.append(x)
return mutable
res = inplace(1)
res = inplace(2)
print(inplace(3))
[1, 2, 3]
def inplace(x, lst=None):
if lst is None: lst=[]
lst.append()
return lst
Еще один пример изменяемого объекта, значение которого поменялось при вызове:
def change_list(seq):
seq[0] = 100
original = [0, 1, 2]
change_list(original)
original
[100, 1, 2]
Дабы не допустить изменения оригинальной последовательности, нужно передать копию изменяемого объекта:
original = [0, 1, 2]
change_list(original[:])
original
[0, 1, 2]
Указание произвольного количества аргументов
Позиционные аргументы
Иногда количество позиционных аргументов может быть переменным. Примерами таких функций могут быть max()
и min()
. Синтаксис для определения таких функций следующий:
def func(pos_params, *args):
block statememt
При вызове функции нужно вводить команду следующим образом:
func(pos_params, arg1, arg2, ...)
Python обрабатывает позиционные аргументы следующим образом: подставляет обычные позиционные аргументы слева направо, а затем помещает остальные позиционные аргументы в кортеж (*args), который можно использовать в функции.
Вот так:
def add_mean(x, *data):
return x + sum(data)/float(len(data))
add_mean(10,0,1,2,-1,0,-1,1,2)
10.5
Если лишние аргументы не указаны, значением по умолчанию будет пустой кортеж.
Произвольное количество аргументов-ключевых слов
Как и в случае с позиционными аргументами можно определять произвольное количество аргументов-ключевых слов следующим образом (в сочетании с произвольным числом необязательных аргументов из прошлого раздела):
def func(pos_params, *args, **kwargs):
block statememt
При вызове функции нужно писать так:
func(pos_params, kw1=arg1, kw2=arg2, ...)
Python обрабатывает аргументы-ключевые слова следующим образом: подставляет обычные позиционные аргументы слева направо, а затем помещает другие позиционные аргументы в кортеж (*args), который можно использовать в функции (см. предыдущий раздел). В конце концов, он добавляет все лишние аргументы в словарь (**kwargs), который сможет использовать функция.
Есть функция:
def print_mean_sequences(**kwargs):
def mean(data):
return sum(data)/float(len(data))
for k, v in kwargs.items():
print k, mean(v)
print_mean_sequences(x=[1,2,3], y=[3,3,0])
y 2.0
x 2.0
Важно, что пользователь также может использовать словарь, но перед ним нужно ставить две звездочки (**):
print_mean_sequences(**{'x':[1,2,3], 'y':[3,3,0]})
y 2.0
x 2.0
Порядок вывода также не определен, потому что словарь не отсортирован.
Документирование функции
Определим функцию:
def sum(s,y): return x + y
Если изучить ее, обнаружатся два скрытых метода (которые начинаются с двух знаков нижнего подчеркивания), среди которых есть __doc__
. Он нужен для настройки документации функции. Документация в Python называется docstring
и может быть объединена с функцией следующим образом:
def sum(x, y):
"""Первая срока - заголовок
Затем следует необязательная пустая строка и текст
документации.
"""
return x+y
Команда docstring
должна быть первой инструкцией после объявления функции. Ее потом можно будет извлекать или дополнять:
print(sum.__doc__)
sum.__doc__ += "some additional text"
Методы, функции и атрибуты, связанные с объектами функции
Если поискать доступные для функции атрибуты, то в списке окажутся следующие методы (в Python все является объектом — даже функция):
sum.func_closure sum.func_defaults sum.func_doc sum.func_name
sum.func_code sum.func_dict sum.func_globals
И несколько скрытых методов, функций и атрибутов. Например, можно получить имя функции или модуля, в котором она определена:
>>> sum.__name__
"sum"
>>> sum.__module
"__main__"
Есть и другие. Вот те, которые не обсуждались:
sum.__call__ sum.__delattr__ sum.__getattribute__ sum.__setattr__
sum.__class__ sum.__dict__ sum.__globals__ sum.__new__ sum.__sizeof__
sum.__closure__ sum.__hash__ sum.__reduce__ sum.__str__
sum.__code__ sum.__format__ sum.__init__ sum.__reduce_ex__ sum.__subclasshook__
sum.__defaults__ sum.__get__ sum.__repr__
Рекурсивные функции
Рекурсия — это не особенность Python. Это общепринятая и часто используемая техника в Computer Science, когда функция вызывает сама себя. Самый известный пример — вычисление факториала n! = n * n — 1 * n -2 * … 2 *1. Зная, что 0! = 1, факториал можно записать следующим образом:
def factorial(n):
if n != 0:
return n * factorial(n-1)
else:
return 1
Другой распространенный пример — определение последовательности Фибоначчи:
f(0) = 1
f(1) = 1
f(n) = f(n-1) + f(n-2)
Рекурсивную функцию можно записать так:
def fibbonacci(n):
if n >= 2:
else:
return 1
Важно, чтобы в ней было была конечная инструкция, иначе она никогда не закончится. Реализация вычисления факториала выше, например, не является надежной. Если указать отрицательное значение, функция будет вызывать себя бесконечно. Нужно написать так:
def factorial(n):
assert n > 0
if n != 0:
return n * factorial(n-1)
else:
return 1
Важно!
Рекурсия позволяет писать простые и элегантные функции, но это не гарантирует эффективность и высокую скорость исполнения.
Если рекурсия содержит баги (например, длится бесконечно), функции может не хватить памяти. Задать максимальное значение рекурсий можно с помощью модуля sys
.
Глобальная переменная
Вот уже знакомый пример с глобальной переменной:
i = 0
def increment():
global i
i += 1
Здесь функция увеличивает на 1 значение глобальной переменной i
. Это способ изменять глобальную переменную, определенную вне функции. Без него функция не будет знать, что такое переменная i
. Ключевое слово global
можно вводить в любом месте, но переменную разрешается использовать только после ее объявления.
За редкими исключениями глобальные переменные лучше вообще не использовать.
Присвоение функции переменной
С существующей функцией func
синтаксис максимально простой:
variable = func
Переменным также можно присваивать встроенные функции. Таким образом позже есть возможность вызывать функцию другим именем. Такой подход называется непрямым вызовом функции.
Менять название переменной также разрешается:
def func(x): return x
a1 = func
a1(10)
10
a2 = a1
a2()
10
В этом примере a1, a2 и func
имеют один и тот же id. Они ссылаются на один объект.
Практический пример — рефакторинг существующего кода. Например, есть функция sq
, которая вычисляет квадрат значения:
def sq(x): return x*x
Позже ее можно переименовать, используя более осмысленное имя. Первый вариант — просто сменить имя. Проблема в том, что если в другом месте кода используется sq
, то этот участок не будет работать. Лучше просто добавить следующее выражение:
square = sq
Последний пример. Предположим, встроенная функция была переназначена:
dir = 3
Теперь к ней нельзя получить доступ, а это может стать проблемой. Чтобы вернуть ее обратно, нужно просто удалить переменную:
del dir
dir()
Анонимная функция: лямбда
Лямбда-функция — это короткая однострочная функция, которой даже не нужно имя давать. Такие выражения содержат лишь одну инструкцию, поэтому, например, if
, for
и while
использовать нельзя. Их также можно присваивать переменным:
product = lambda x,y: x*y
В отличие от функций, здесь не используется ключевое слово return
. Результат работы и так возвращается.
С помощью type()
можно проверить тип:
>>> type(product)
function
На практике эти функции редко используются. Это всего лишь элегантный способ записи, когда она содержит одну инструкцию.
power = lambda x=1, y=2: x**y
square = power
square(5.)
25
power = lambda x,y,pow=2: x**pow + y
[power(x,2, 3) for x in [0,1,2]]
[2, 3, 10]
Изменяемые аргументы по умолчанию
>>> def foo(x=[]):
... x.append(1)
... print x
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]
Вместо этого нужно использовать значение «не указано» и заменить на изменяемый объект по умолчанию:
>>> def foo(x=None):
... if x is None:
... x = []
... x.append(1)
... print x
>>> foo()
[1]
>>> foo()
[1]
Назад в начало
Функция – это именованный блок кода, к которому можно обратиться из любого места программы. У функции есть имя и список входных параметров, а также возвращаемое значение.
Функция позволяет использовать в программе один и тот же фрагмент кода несколько раз.
Oбъявление функции в Python выглядит так:
def function_name(argument1, argument2, …):
# код функции
# def – DEclare Function – “объявить функцию”
# function_name – имя функции
# (argument1, argument2, …) – список аргументов, поступающих на вход функции при ее вызове
# тело функции – это весь код, который идет после двоеточия
# Объявление функции
def hello(name):
print(‘Hello, ‘ + name)
# Вызовы функции
hello(‘Max’)
hello(‘Ivan’)
hello(‘Alex’)
hello(‘Kate’)
# Вывод
>> Hello, Max
>> Hello, Ivan
>> Hello, Alex
>> Hello, Kate
Оператор return возвращает значение из функции.
Представьте себе обычный калькулятор. Вы вводите первое число, операцию и второе число. Калькулятор возвращает нам результат операции над этими числами. Всё просто, не так ли? Функции точно так же умеют возвращать значение с помощью специального оператора return.
# Объявление функции
def sum2(a, b):
return a + b
# Вызовы функции
s1 = sum2(10, 2)
s2 = sum2(108, 100)
s3 = sum2(3, 1)
print(f‘s1 = {s1}‘)
print(f‘s2 = {s2}‘)
print(f‘s3 = {s3}‘)
>> s1 = 12
>> s2 = 208
>> s3 = 4
# Функция умножения двух чисел
def mult2(a, b):
return a * b
# Вызовем нашу функцию
m1 = mult2(10, 2)
m2 = mult2(108, 100)
m3 = mult2(3, 1)
print(f‘m1 = {m1}‘)
print(f‘m2 = {m2}‘)
print(f‘m3 = {m3}‘)
>> m1 = 20
>> m2 = 10800
>> m3 = 3
Для параметров функции можно указывать значения по умолчанию. Это дает возможность вызывать функцию с меньшим числом параметров.
# Аргумент name по умолчанию равен ‘world’
def hello(name=‘world’):
print(‘Hello, ‘ + name)
hello()
hello(‘Ivan’)
>> Hello, world
>> Hello, Ivan
Примеры
1. Квадрат
# Написать функцию square(), вычисляющую квадрат числа.
def square(number):
return number * number # Возвращаем результат работы функции обратно в программу
a = square(2)
print(a)
>> 4
2. Периметр
# Напишите функцию perimetr, вычисляющую периметр прямоугольника со сторонами a и b.
def perimetr(a, b):
return 2 * (a + b)
p = perimetr(4, 3)
print(p)
>> 14
3. Четное число
# Напишите функцию isEven, возвращающую True, если число четное, и False, если – нечетное.
def isEven(x):
return x % 2 == 0
print(isEven(10))
print(isEven(11))
>> True
>> False
4. Сумма списка
# Напишите функцию amountList, которая возвращает сумму всех элементов списка.
def amountList(lst):
amount = 0
for x in lst:
amount += x
return amount
print(amountList([1, 2, 3]))
mylist = [1, 2, 4, 8, 16]
s = amountList(mylist)
print(f‘Сумма списка {mylist} равна {s}‘)
>> 6
>> Сумма списка [1, 2, 4, 8, 16] равна 31
5. Фибоначчи
# Напишите функцию fib, которая возвращает n-ное число Фибоначчи.
# Последовательность Фибоначчи выглядит так: 1 1 2 3 5 8 13 21 34
def fib(n):
a, b = 0, 1
if n == 0: return 0
for i in range(1, n):
a, b = b, a + b
return b
print(fib(2))
print(fib(3))
print(fib(4))
print(fib(5))
print(fib(10))
>> 1
>> 2
>> 3
>> 5
>> 55
6. Факториал
# Напишите функцию fact, вычисляющую значение факториала числа N.
# Факториал числа – это произведение всех чисел от 1 до N.
# Например, факториал числа 5 равен 120 (5! = 120).
def fact(n):
result = 1
while n > 1:
result *= n
n -= 1
return result
print(fact(2))
print(fact(3))
print(fact(4))
print(fact(5))
>> 2
>> 6
>> 24
>> 120
Решение задач
1. Площадь круга
Напишите функцию, которая получает в качестве аргумента радиус круга и находит его площадь.
# Не забудьте написать функцию circle…
print(circle(4))
print(circle(1))
# Вывод:
>> 50.24
>> 3.14
2. На три
Напишите функцию, которая возвращает True, если число делится на 3, и False, если – нет.
# Не забудьте написать функцию three…
print(three(4))
print(three(3))
# Вывод:
>> False
>> True
3. Максимум в списке
Напишите функцию, которая возвращает максимальный элемент из переданного в нее списка.
# Напишите функцию maxList…
mylist = [1, 3, 2]
print(maxList(mylist))
# Вывод:
>> 3
4. Сколько четных
Напишите функцию, которая возвращает количество четных элементов в списке.
# Напишите функцию evenCounter…
mylist = [1, 10, 2, 4, 6]
evens = evenCounter(mylist)
print(even)
# Вывод:
>> 4
5. Уникальные
Напишите функцию, которая возвращает список с уникальными (неповторяющихся) элементам.
# Напишите функцию unique…
mylist = [1, 1, 2, 1, 3, 2, 3]
print(unique(mylist))
# Вывод:
>> [1, 2, 3]
Python позволяет разделить большую программу на основные строительные блоки, известные как функции.
Функции в Python – это организованный блок многократно используемого кода, который можно вызывать при необходимости.
Функция содержит набор программных операторов, заключенных в {}. Функцию можно вызывать несколько раз, чтобы обеспечить возможность повторного использования и модульность программы Python.
Функция помогает программисту разбить программу на более мелкие части. Она очень эффективно организует код и избегает повторения кода. По мере роста программы функция делает ее более организованной.
Python предоставляет нам различные встроенные функции, такие как range() или print(). Пользователь также может создавать свои функции, которые можно назвать пользовательскими функциями.
В основном есть два типа функций:
- Функции, определяемые пользователем. Определяются пользователем для выполнения конкретной задачи.
- Встроенные функции. Это те функции, которые предварительно определены в Python.
В этом руководстве мы обсудим функции, определяемые пользователем.
Преимущество функций в Python
У функций Python есть следующие преимущества:
- Используя функции, мы можем избежать повторной перезаписи одной и той же логики / кода снова и снова в программе.
- Мы можем вызывать функции Python несколько раз и в любом месте программы.
- Мы можем легко отслеживать большую программу Python, если она разделена на несколько функций.
- Возможность повторного использования – главное достижение.
- Однако вызов функции всегда является накладным.
Создание
Python предоставляет ключевое слово def для определения функции. Синтаксис функции определения приведен ниже.
Синтаксис:
def my_function(parameters): function_block return expression
Давайте разберемся с синтаксисом определения функций.
- Ключевое слово def вместе с именем функции используется для определения функции.
- Правило идентификатора должно следовать за именем функции.
- Функция принимает параметр(аргумент), и они могут быть необязательными.
- Функциональный блок начинается с двоеточия(:), и операторы блока должны иметь такой же отступ.
- Оператор return используется для возврата значения. Функция может иметь только один возврат.
Вызов функции
В Python после создания функции мы можем вызывать ее из другой функции. Перед вызовом функция должна быть определена; в противном случае интерпретатор Python выдает ошибку. Чтобы вызвать функцию, используйте имя функции, за которым следуют круглые скобки.
Рассмотрим простой пример, который выводит сообщение «Hello World».
#function definition def hello_world(): print("hello world") # function calling hello_world()
Выход:
hello world
Оператор возврата return
Оператор return используется в конце функции и возвращает результат функции. Он завершает выполнение функции и передает результат туда, где функция вызывается. Оператор return не может использоваться вне функции.
Синтаксис:
return [expression_list]
Он может содержать выражение, которое оценивается, и значение возвращается вызывающей функции. Если оператор return не имеет выражения или не существует сам по себе в функции, он возвращает объект None.
Рассмотрим следующие примеры:
Пример 1.
# Defining function def sum(): a = 10 b = 20 c = a+b return c # calling sum() function in print statement print("The sum is:",sum())
Выход:
The sum is: 30
В приведенном выше коде мы определили функцию с именем sum, и у нее есть оператор c = a + b, который вычисляет заданные значения, а результат возвращается оператором return вызывающей функции.
Пример 2. Создание функции без оператора возврата
# Defining function def sum(): a = 10 b = 20 c = a+b # calling sum() function in print statement print(sum())
Выход:
None
В приведенном выше коде мы определили ту же функцию без оператора return, так как мы видим, что функция sum() вернула объект None вызывающей функции.
Аргументы в функции
Аргументы – это типы информации, которые можно передать функции. Аргументы указаны в скобках. Мы можем передать любое количество аргументов, но они должны быть разделены запятой.
Рассмотрим следующие примеры, содержащий функцию, которая принимает строку в качестве аргумента.
Пример 1.
#defining the function def func(name): print("Hi ",name) #calling the function func("Devansh")
Выход:
Hi Devansh
Пример 2.
#Python function to calculate the sum of two variables #defining the function def sum(a,b): return a+b; #taking values from the user a = int(input("Enter a: ")) b = int(input("Enter b: ")) #printing the sum of a and b print("Sum = ",sum(a,b))
Выход:
Enter a: 10 Enter b: 20 Sum = 30
Вызов по ссылке в Python
В Python вызов по ссылке означает передачу фактического значения в качестве аргумента функции. Все функции вызываются по ссылке, т. е. все изменения, внесенные в ссылку внутри функции, возвращаются к исходному значению, на которое ссылается ссылка.
Пример 1. Передача неизменяемого объекта(списка)
#defining the function def change_list(list1): list1.append(20) list1.append(30) print("list inside function = ",list1) #defining the list list1 = [10,30,40,50] #calling the function change_list(list1) print("list outside function = ",list1)
Выход:
list inside function = [10, 30, 40, 50, 20, 30]
list outside function = [10, 30, 40, 50, 20, 30]
Пример 2. Передача изменяемого объекта(строки)
#defining the function def change_string(str): str = str + " Hows you " print("печать строки внутри функции :",str) string1 = "Hi I am there" #calling the function change_string(string1) print("печать строки вне функции :",string1)
Выход:
печать строки внутри функции: Hi I am there Hows you
печать строки вне функции: Hi I am there.
Типы аргументов
Может быть несколько типов аргументов, которые могут быть переданы во время вызова функции.
- Обязательные аргументы.
- Аргументы ключевого слова.
- По умолчанию.
- Переменной длины.
Обязательные
Мы можем предоставить аргументы во время вызова функции. Обязательные аргументы – это аргументы, которые необходимо передать во время вызова функции с точным совпадением их позиций в вызове функции и определении функции. Если какой-либо из аргументов не указан в вызове функции или положение аргументов изменено, интерпретатор Python покажет ошибку.
Рассмотрим следующие примеры.
Пример 1.
def func(name): message = "Hi "+name return message name = input("Enter the name:") print(func(name))
Выход:
Введите имя: Джон
привет, Джон
Пример 2.
#the function simple_interest accepts three arguments and returns the simple interest accordingly def simple_interest(p,t,r): return(p*t*r)/100 p = float(input("Enter the principle amount? ")) r = float(input("Enter the rate of interest? ")) t = float(input("Enter the time in years? ")) print("Simple Interest: ",simple_interest(p,r,t))
Выход:
Введите основную сумму: 5000
Введите процентную ставку: 5
Введите время в годах: 3
Простой процент: 750,0
Пример 3
#the function calculate returns the sum of two arguments a and b def calculate(a,b): return a+b calculate(10) # this causes an error as we are missing a required arguments b.
Выход:
TypeError: calculate() отсутствует 1 обязательный позиционный аргумент: 'b'
Аргументы по умолчанию
Python позволяет нам инициализировать аргументы при определении функции. Если значение любого из аргументов не предоставляется во время вызова функции, то этот аргумент может быть инициализирован значением, указанным в определении, даже если аргумент не указан при вызове функции.
Пример 1.
def printme(name,age=22): print("My name is",name,"and age is",age) printme(name = "john")
Выход:
My name is John and age is 22
Пример 2.
def printme(name,age=22): print("My name is",name,"and age is",age) printme(name = "john") #the variable age is not passed into the function however the default value of age is considered in the function printme(age = 10,name="David") #the value of age is overwritten here, 10 will be printed as age
Выход:
My name is john and age is 22
My name is David and age is 10
Аргументы переменной длины(* args)
В больших проектах иногда мы можем не знать количество аргументов, которые нужно передать заранее. В таких случаях Python предоставляет нам гибкость, предлагая значения, разделенные запятыми, которые внутренне обрабатываются как кортежи при вызове функции. Используя аргументы переменной длины, мы можем передавать любое количество аргументов.
Однако при определении функции мы определяем аргумент переменной длины, используя * args(звездочку) как * <переменная – имя>.
Рассмотрим следующий пример:
def printme(*names): print("type of passed argument is ",type(names)) print("printing the passed arguments...") for name in names: print(name) printme("john","David","smith","nick")
Выход:
type of passed argument is <class 'tuple'>
printing the passed arguments...
john
David
smith
nick
В приведенном выше коде мы передали * имена в качестве аргумента переменной длины. Мы вызвали функцию и передали значения, которые внутри обрабатываются как кортежи. Кортеж – это итеративная последовательность, такая же, как и список. Чтобы напечатать заданные значения, мы повторили имена * arg, используя цикл for в Python.
Аргументы ключевого слова(** kwargs)
Python позволяет нам вызывать функцию с аргументами ключевых слов. Такой вызов функции позволит нам передавать аргументы в случайном порядке.
Имя аргументов рассматривается как ключевое слово и сопоставляется при вызове и определении функции. Если такое же совпадение найдено, значения аргументов копируются в определение функции.
Рассмотрим следующие примеры.
Пример 1.
#function func is called with the name and message as the keyword arguments def func(name,message): print("printing the message with",name,"and ",message) #name and message is copied with the values John and hello respectively func(name = "John",message="hello")
Выход:
printing the message with John and hello
Пример 2. С указанием значений в другом порядке при вызове.
#The function simple_interest(p, t, r) is called with the keyword arguments the order of arguments doesn't matter in this case def simple_interest(p,t,r): return(p*t*r)/100 print("Simple Interest: ",simple_interest(t=10,r=10,p=1900))
Выход:
Simple Interest: 1900.0
Если мы предоставим другое имя аргументов во время вызова функции, будет выдана ошибка.
Рассмотрим следующий пример.
Пример 3.
#The function simple_interest(p, t, r) is called with the keyword arguments. def simple_interest(p,t,r): return(p*t*r)/100 # doesn't find the exact match of the name of the arguments(keywords) print("Simple Interest: ",simple_interest(time=10,rate=10,principle=1900))
Выход:
TypeError: simple_interest() got an unexpected keyword argument 'time'
Python позволяет нам предоставлять сочетание необходимых аргументов и аргументов ключевого слова во время вызова функции. Однако обязательный аргумент не должен указываться после аргумента ключевого слова, т. е. после того, как аргумент ключевого слова встречается в вызове функции, следующие аргументы также должны быть аргументами ключевого слова.
Рассмотрим следующие примеры.
Пример 4.
def func(name1,message,name2): print("printing the message with",name1,",",message,",and",name2) #the first argument is not the keyword argument func("John",message="hello",name2="David")
Выход:
printing the message with John , hello ,and David
Следующий пример вызовет ошибку из-за неправильного сочетания ключевого слова и требуемых аргументов, переданных в вызове функции.
Пример 5.
def func(name1,message,name2): print("printing the message with",name1,",",message,",and",name2) func("John",message="hello","David")
Выход:
SyntaxError: positional argument follows keyword argument
Python предоставляет возможность передавать несколько аргументов ключевого слова, которые могут быть представлены как ** kwargs. Похож на * args, но сохраняет аргумент в формате словаря. Этот тип аргументов полезен, когда мы не знаем заранее количество аргументов.
Рассмотрим следующий пример:
Пример 6. Многие аргументы используют аргумент ключевого слова.
def food(**kwargs): print(kwargs) food(a="Apple") food(fruits="Orange", Vagitables="Carrot")
Выход:
{'a': 'Apple'} {'fruits': 'Orange', 'Vagitables': 'Carrot'}
Объем переменных
Объем переменных зависит от места, где переменная объявляется. Переменная, объявленная в одной части программы, может быть недоступна для других частей.
В Python переменные определяются с двумя типами областей видимости:
- Глобальны.
- Локальные.
Известно, что переменная, определенная вне любой функции, имеет глобальную область видимости, тогда как переменная, определенная внутри функции, имеет локальную область видимости.
Рассмотрим следующий пример.
Пример 1. Локальная переменная
def print_message(): message = "hello !! I am going to print a message." # the variable message is local to the function itself print(message) print_message() print(message) # this will cause an error since a local variable cannot be accessible here.
Выход:
hello !! I am going to print a message.
File "/root/PycharmProjects/PythonTest/Test1.py", line 5, in
print(message)
NameError: name 'message' is not defined
Пример 2. Глобальная переменная
def calculate(*args): sum=0 for arg in args: sum = sum +arg print("The sum is",sum) sum=0 calculate(10,20,30) #60 will be printed as the sum print("Value of sum outside the function:",sum) # 0 will be printed Output:
Выход:
The sum is 60
Value of sum outside the function: 0
Изучаю Python вместе с вами, читаю, собираю и записываю информацию опытных программистов.
Содержание:развернуть
- Синтаксис
- Термины и определения
- Важность функций
-
Абстракция
-
Возможность повторного использования
-
Модульность
-
Пространство имен
- Объявление и вызов функций
- Область видимости функций
-
Локальная (L)
-
Область объемлющих функций (E)
-
Глобальная (G)
- Аргументы
-
Позиционные
-
Именованные
-
Необязательные параметры (параметры по умолчанию)
-
Аргументы переменной длины (args, kwargs)
-
Передача по значению и по ссылке
-
Словарь в качестве аргументов (упаковка)
- Возвращаемые значения (return)
-
Что можно возвращать
-
Распаковка возвращаемых значений
-
Пустая функция
- Чистые функции и побочные эффекты
- Lambda функции
- Docstring
- Аннотация типов
- Функции vs процедуры — в чем отличие?
- Время выполнения функции
- Вложенные функции и рекурсия
Функция — это фрагмент программного кода, который решает какую-либо задачу.
Его можно вызывать в любом месте основной программы. Функции помогают избегать дублирования кода при многократном его использовании. А также имеют ряд других преимуществ, описанных ниже.
Синтаксис
💁♀️ Простой пример: Вы торгуете мёдом, и после каждой продажи вам нужно печатать чек. В нём должно быть указано: название фирмы, дата продажи, список наименований проданных товаров, их количество, цены, общая сумма, а также сакраментальная фраза “Спасибо за покупку!”.
Если не пользоваться функциями, всё придётся прописывать вручную. В простейшем случае программа будет выглядеть так:
print("ООО Медовый Гексагон")
print("Мёд липовый", end=" ")
print(1, end="шт ")
print(1250, end="р")
print("nCумма", 1250, end="р")
print("nСпасибо за покупку!")
А теперь представьте, что произойдёт, когда вы раскрутитесь, и покупатели станут приходить один за другим. В таком случае, чеки надо будет выдавать очень быстро. Но что делать, если вдруг нагрянет ваш любимый клиент и купит 10 сортов мёда в разных количествах? Далеко не все в очереди согласятся ждать, пока вы посчитаете общую сумму и внесёте её в чек.
Хорошо, что данный процесс можно легко оптимизировать с использованием функций.
def print_check(honey_positions):
sum = 0 # переменная для накопления общей суммы
print("ООО Медовый Гексагонn")
# в цикле будем выводить название, количество и цену
for honey in honey_positions:
name = honey[0]
amount = honey[1]
price = honey[2]
print(f"{name} ({amount} шт.) - {price} руб.")
sum += amount * price # здесь же будем считать ещё и общую сумму
print(f"nИтого: {sum} руб.")
print("Спасибо за покупку!")
Встаёт резонный вопрос: где же обещанное упрощение и куда подевались товары? Как раз для этого, мы и будем описывать состав покупки не напрямую в функции, а в отдельном списке кортежей. Каждый кортеж состоит из трёх элементов: название товара, количество и цена.
# (название, количество, цена за штуку)
honey_positions = [
("Мёд липовый", 3, 1250),
("Мёд цветочный", 7, 1000),
("Мёд гречишный", 6, 1300),
("Донниковый мёд", 1, 1750),
("Малиновый мёд", 10, 2000),
]
Теперь этот список передадим в функцию как аргумент, и самостоятельно считать больше не придётся.
print_check(honey_positions)
>
ООО Медовый Гексагон
Мёд липовый (3 шт.) - 1250 руб.
Мёд цветочный (7 шт.) - 1000 руб.
Мёд гречишный (6 шт.) - 1300 руб.
Донниковый мёд (1 шт.) - 1750 руб.
Малиновый мёд (10 шт.) - 2000 руб.
Итого: 40300 руб.
Спасибо за покупку!
Да, код стал более массивным. Однако теперь для печати чека вам не придётся самостоятельно вычислять итог. Достаточно лишь изменить количество и цену товаров в списке. Существенная экономия времени! Слава функциям!
Термины и определения
Ключевое слово def
в начале функции сообщает интерпретатору о том, что следующий за ним код — есть её определение. Всё вместе — это объявление функции.
# объявим функцию my_function()
def my_function():
# тело функции
Аргументы часто путают с параметрами:
- Параметр — это переменная, которой будет присваиваться входящее в функцию значение.
- Аргумент — само это значение, которое передается в функцию при её вызове.
# a, b - параметры функции
def test(a, b):
# do something
# 120, 404 — аргументы
test(120, 404)
Ключевая особенность функций — возможность возвращать значение
Для этого используется слово return
. Предположим, вы часто умножаете числа. Вы не осведомлены заранее, целые они или вещественные, но хотите, чтобы результат был целым всегда. Решим задачу с помощью функции:
# она будет принимать два множителя, а возвращать их округленное
# до целого числа произведение
def int_multiple(a, b):
product = a * b
# возвращаем значение
return int(product)
print(int_multiple(341, 2.7))
> 920
☝️ Главная фишка возвращаемых значений в том, что их можно использовать в дальнейшем коде: присваивать переменным, совершать с ними разные операции и передавать как аргументы в другие функции.
# найдём квадратный корень из возврата функции int_multiple
# во встроенную функцию sqrt() мы передали вызов int_multiple
print(math.sqrt(int_multiple(44, 44)))
> 44
Важность функций
Абстракция
Человек бежит, машина едет, корабль плывёт, а самолёт летит. Всё это — объекты реального мира, которые выполняют однотипные действия. В данном случае, они перемещаются во времени и пространстве. Мы можем абстрагироваться от их природы, и рассматривать эти объекты с точки зрения того, какое расстояние они преодолели, и сколько времени на это ушло.
Мы можем написать функцию, которая вычисляет скорость в каждом конкретном случае. Нам не важно, кто совершает движение: и для человека и для самолёта средняя скорость будет рассчитываться одинаково.
def calculate_speed(distance, time):
return distance / time
Это простой пример и простая функция, но абстракции могут быть куда более сложными. И именно тогда раскрывается настоящая сила функций. Вместо того чтобы решать задачу для каждого конкретного случая, проще написать функцию, которая находит решение для целого ряда однотипных, в рамках применяемой абстракции, объектов. В случае сложных и длинных вычислений, это повлечёт за собой значительное сокращение объёмов кода, а значит и времени на его написание.
Возможность повторного использования
Функции были созданы ради возможности их многократного применения. Код без функций превратился бы в огромное нечитаемое полотно, на порядки превышающее по длине аналогичную программу с их использованием.
Например, при работе с массивами чисел, вам нужно часто их сортировать. Вместо того чтобы реализовать простой алгоритм сортировки (или использовать встроенную функцию), вам пришлось бы каждый раз перепечатывать тело этой или похожей функции:
# пузырьковая сортировка
def bubble_sort(nums):
for i in range(0, len(nums) - 1):
for j in range(len(nums) - 1):
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j]
return nums
Всего 10 таких сортировок, и привет, лишние 50 строк кода.
Модульность
Разбитие больших и сложных процессов на простые составляющие — важная часть, как кодинга, так и реальной жизни. В повседневности мы занимаемся этим неосознанно. Когда убираемся в квартире, мы пылесосим, моем полы и окна, очищаем поверхности от пыли и наводим блеск на всё блестящее. Всё это — составляющие одного большого процесса под названием “уборка”, но каждую из них также можно разбить на более простые подпроцессы.
В программировании модульность строится на использовании функций. Для каждой подзадачи — своя функция. Такая компоновка в разы улучшает читабельность кода и уменьшает сложность его дальнейшей поддержки.
Допустим, мы работаем с базой данных. Нам нужна программа, которая считывает значения из базы, обрабатывает их, выводит результат на экран, а затем записывает его обратно в базу.
Без применения модульности получится сплошная последовательность инструкций:
# Database operation program
# Код для чтения данных из базы
# ...
# ...
# Код для выполнения операций над данными
# ...
# ...
# Код для вывода результата
# ...
# ...
# Код для записи данных в базу
# ...
# ...
Но если вынести каждую операцию в отдельную функцию, то текст главной программы получится маленьким и аккуратным.
def read_from_db():
# Код для чтения данных из базы
# ...
# ...
# ...
def operate_with_data():
# Код для выполнения операций над данными
# ...
# ...
# ...
def print_result():
# Код для вывода результата
# ...
# ...
# ...
def write_to_db():
# Код для записи данных в базу
# ...
# ...
# ...
# код основной программы
# Database operation program
read_from_db()
operate_with_data()
print_result()
write_to_db()
Это и называется модульностью.
Пространство имен
Концепция пространства имён расширяет понятие модульности. Однако цель — не облегчить читаемость, а избежать конфликтов в названиях переменных.
💁♀️ Пример из жизни: в ВУЗе учатся два человека с совпадающими ФИО. Их нужно как-то различать. Если сделать пространствами имён группы этих студентов, то проблема будет решена. В рамках своей группы ФИО этих студентов будут уникальными.
Объявление и вызов функций
Объявим функцию:
def hello():
print('Adele is cute')
После того как мы это сделали, функцию можно вызвать в любой части программы, но ниже самого объявления.
# код выполняется последовательно, поэтому сейчас интерпретатор
# не знает о существовании функции hello
hello()
def hello():
print('Adele is cute')
> NameError: name 'hello' is not defined
Поэтому стоит лишь поменять объявление и вызов местами, и всё заработает:
def hello():
print('Adele is cute')
hello()
> Adele is cute
Область видимости функций
Рассмотрим подробнее области видимости:
Локальная (L)
Локальная область видимости находится внутри def
:
def L():
# переменная i_am_local является локальной внутри L()
i_am_local = 5
Область объемлющих функций (E)
Объявили функцию e()
. Внутри неё объявили функцию inner_e()
. Относительно inner_e()
все переменные, объявленные в e()
будут относиться к области объемлющих функций. Такие переменные являются нелокальными в inner_e()
. Чтобы с ними взаимодействовать, нужно использовать ключевое слово nonlocal
:
def e():
x = 5
def inner_e():
nonlocal x
x = x + 1
return x
return inner_e()
print(e())
> 6
Глобальная (G)
Глобальная область видимости лежит за пределами всех def
.
# G
num = 42
def some_function(n):
res = n + num
return res
print(some_function(1))
> 43
Аргументы
Позиционные
Вспомним, аргумент — это конкретное значение, которое передаётся в функцию. Аргументом может быть любой объект. Он может передаваться, как в литеральной форме, так и в виде переменной.
Значения в позиционных аргументах подставляются согласно позиции имён аргументов:
nums = [42, 11, 121, 13, 7]
state = True
# в данном примере
# 1-я позиция "nums" -> parameter_1
# 2-я позиция "state" -> parameter_2
def test_params(parameter_1, parameter_2):
pass
# равнозначные варианты вызова функции
test_params(nums, state)
test_params([42, 11, 121, 13, 7], True)
Именованные
Пусть есть функция, принимающая три аргумента, а затем выводящая их на экран. Python позволяет явно задавать соответствия между значениями и именами аргументов.
def print_trio(a, b, c):
print(a, b, c)
print_trio(c=4, b=5, a=6)
> 6 5 4
При вызове соответствие будет определяться по именам, а не по позициям аргументов.
Необязательные параметры (параметры по умолчанию)
Python позволяет делать отдельные параметры функции необязательными. Если при вызове значение такого аргумента не передается, то ему будет присвоено значение по умолчанию.
def not_necessary_arg(x='My', y='love'):
print(x, y)
# если не передавать в функцию никаких значений, она отработает со значениями по умолчанию
not_necessary_arg()
> My love
# переданные значения заменяют собой значения по умолчанию
not_necessary_arg(2, 1)
> 2 1
Аргументы переменной длины (args, kwargs)
Когда заранее неизвестно, сколько конкретно аргументов будет передано в функцию, мы пользуемся аргументами переменной длины. Звёздочка “*” перед именем параметра сообщает интерпретатору о том, что количество позиционных аргументов будет переменным:
def infinity(*args):
print(args)
infinity(42, 12, 'test', [6, 5])
> (42, 12, 'test', [6, 5])
Переменная args
составляет кортеж из переданных в функцию аргументов.
Функции в питоне могут также принимать и переменное количество именованных аргументов. В этом случае перед названием параметра ставится “**
“:
def named_infinity(**kwargs):
print(kwargs)
named_infinity(first='nothing', second='else', third='matters')
> {'first': 'nothing', 'second': 'else', 'third': 'matters'}
Здесь kwargs уже заключает аргументы не в кортеж, а в словарь.
Передача по значению и по ссылке
В Python аргументы могут быть переданы, как по ссылке, так и по значению. Всё зависит от типа объекта.
Если объект неизменяемый, то он передаётся в функцию по значению. Неизменяемые объекты это:
- Числовые типы (int, float, complex).
- Строки (str).
- Кортежи (tuple).
num = 42
def some_function(n):
# в "n" передается значение переменной num (42)
n = n + 10
print(n)
some_function(num)
print(num) # "num" по прежнему содержит 42
>
52
42
Изменяемые объекты передаются в функцию по ссылке. Изменяемыми они называются потому что их содержимое можно менять, при этом ссылка на сам объект остается неизменной.
В Python изменяемые объекты это:
- Списки (list).
- Множества (set).
- Словари (dict).
num = [42, 43, 44]
def some_function(n):
# в "n" передается ссылка на переменную "num".
# "n" и "num" ссылаются на один и тот же объект
n[0] = 0
print(n)
some_function(num)
print(num) # "num" изменился
>
[0, 43, 44]
[0, 43, 44]
Будьте внимательны при передаче изменяемых объектов. Одна из частых проблем новичков.
💭 В функциональном программировании существует понятие “функциями с побочными эффектами” — когда функция в процессе своей работы изменяет значения глобальных переменных. По возможности, избегать таких функций.
Словарь в качестве аргументов (упаковка)
Передаваемые в функцию аргументы можно упаковать в словарь при помощи оператора “**”:
def big_dict(**arguments):
print(arguments)
big_dict(key='value')
> {'key': 'value'}
Возвращаемые значения (return)
Что можно возвращать
Функции в Python способны возвращать любой тип объекта.
Распаковка возвращаемых значений
В Питоне поддерживается возврат функциями сразу несколько значений. Достаточно перечислить их через запятую после инструкции return
. Возвращаемым типом будет кортеж (tuple
), который можно распаковать в переменные.
def calculate(num1, num2):
return num1 + num2, num1 - num2, num1 * num2
# для так называемой распаковки нескольких значений
# их следует присвоить равному количеству аргументов
res1, res2, res3 = calculate(7, 6)
print(res1, res2, res3)
> 13 1 42
print(type(calculate(7, 6)))
<class 'tuple'>
☝️ Обратите внимание, что количество возвращаемых значение в кортеже должно совпадать с количеством переменных при распаковке. Иначе произойдет ошибка:
def calculate(num1, num2):
return num1 + num2, num1 - num2
# для так называемой распаковки нескольких значений
# их следует присвоить равному количеству аргументов
res1, res2, res3 = calculate(7, 6)
print(res1, res2, res3)
>
ValueError: not enough values to unpack (expected 3, got 2)
Пустая функция
Иногда разработчики оставляют реализацию на потом, и чтобы объявленная функция не генерировала ошибки из-за отсутствия тела, в качестве заглушки используется ключевое слово pass
:
def empty():
pass
Чистые функции и побочные эффекты
Немного функционального программирования. Есть такие функции, которые при вызове меняют файлы и таблицы баз данных, отправляют данные на сервер или модифицируют глобальные переменные. Всё это — побочные эффекты.
У чистых функций побочных эффектов нет. Такие функции не изменяют глобальные переменные в ходе выполнения, не рассылают и не выводят на печать никакие данные, не касаются объектов, и так далее.
Чистые функции производят вычисления по заданным аргументам и возвращают зависящий только от них самих результат.
Lambda функции
Кроме инструкции def
в питоне можно создавать объекты функций в виде выражений. Так называемые анонимные функции создаются с помощью инструкции lambda
. Чаще всего их применяют для получения встроенной функции или же для отложенного выполнения фрагмента программного кода.
lambda_test = lambda a, b: pow(a, b)
print(lambda_test(2, 4))
> 16
Docstring
Документировать код — особое искусство. Оно существует параллельно с разработкой и сопоставимо с ней по важности. Поэтому нередко документации в программе больше, чем самого кода.
Когда над проектом работает большая команда, а может и не одна, да и еще и много лёт подряд, то значение и важность документации возрастают прямо пропорционально.
Подробнее про docstring вы можете почитать тут:
Аннотация типов
Python — язык с динамической типизацией. По этой причине вполне возможны ситуации, когда вопреки ожиданиям разработчика в функцию подаются, например, не целые числа, а, допустим, строки. Чтобы отслеживать подобные случаи и сильнее контролировать процесс выполнения программы, была изобретена аннотация типов.
С помощью аннотации типов мы указываем, что параметры в функции имеют строго определенный тип.
def prod(a: int, b: int) -> int:
return a * b
В этой функции мы показали, что аргументы и результат должны быть целыми. Если передать float
, то функция выполнится как обычно, однако IDE предупредит нас, что было получено неожиданное значение.
При этом интерпретатор считывает аннотации типов, но никак их не обрабатывает.
Функции vs процедуры — в чем отличие?
Для языка нет различий между функциями и процедурами. Но с точки зрения программиста — это разные сущности.
Отличие в том, что функции возвращают значение, а процедуры — нет. Отсюда вытекают и разные области их применения и смысл использования. Скажем, производить некие вычисления в процедуре бессмысленно.
def proc(i, j):
pow(i, j)
proc(1, 200)
Она успешно отработает, но не вернёт нам результат. Поэтому добавляем ключевое слово return
, и вот этот код обретает смысл:
def func(i, j):
return pow(i, j)
print(func(3, 2))
> 9
И наоборот, оформлять набор инструкций, выполняющий некую обработку, в виде функции также лишено смысла:
def print_low_word(word):
print(word.lower())
return 0
s = 'GOOD'
print_low_word(s)
> good
Возвращаемое значение не представляет собой никакой ценности, поэтому print_low_word(s)
лучше оформить, как процедуру.
Время выполнения функции
Чтобы оценить время выполнения функции, можно поместить её вызов внутрь следующего кода:
from datetime import datetime
import time
start_time = datetime.now()
# здесь вызываем функцию
time.sleep(5)
print(datetime.now() - start_time)
Вложенные функции и рекурсия
Функции, которые объявляются и вызываются внутри других функций, называются вложенными.
def outerFunc():
def firstInner():
print('This is first inner function')
def secondInner():
print('This is second inner function')
firstInner()
secondInner()
outerFunc()
> This is first inner function
> This is second inner function
Рекурсия является частным случаем вложенной функции. Это функция, которая вызывает саму себя.
# посчитаем сумму чисел от 1 до num
def sum_from_one(num):
if num == 1:
return 1
return num + sum_from_one(num - 1)
print(sum_from_one(5))
> 15
😉
Все курсы > Программирование на Питоне > Занятие 6
Функция в программировании не совсем то же самое, что функция в математике. В математике функция принимает одно значение и возвращает другое. В программировании — это программа в программе, отдельный участок кода, к которому можно обратиться по имени.
Зачем это нужно? Если у вас есть код, который используется несколько раз в рамках программы, то функция позволяет написать код один раз, а потом просто вызывать его при необходимости. Так мы не дублируем код и лучше его структурируем.
Откроем ноутбук к этому занятию⧉
Встроенные функции
В Питоне есть множество встроенных в (1) стандартный функционал (built-in functions) и (2) дополнительные библиотеки (library functions) функций, и мы много раз их использовали.
Рассмотрим функцию для создания гистограммы plt.hist(). Вначале импортируем библиотеки.
import matplotlib.pyplot as plt import numpy as np |
Сгенерируем данные, которые передадим этой функции (эти же данные мы создавали и использовали на восьмом занятии вводного курса).
# установим точку отсчета для воспроизведения такого же результата np.random.seed(42) # и сгенерируем данные о росте height = list(np.round(np.random.normal(180, 10, 1000))) |
Теперь построим гистограмму передав ей в качестве параметров и аргументов наши данные и количество интервалов.
plt.hist(height, bins = 10) plt.show() |
Как мы видим, достаточно обратиться к соответствующей библиотеке (
plt), вызвать эту функцию по имени (
hist) и задать параметры и их аргументы (
heightи
bins = 10), и будет исполнен тот код, который заложили в нее создатели библиотеки Matplotlib.
Теперь несколько слов про параметры и аргументы функции.
Параметры и аргументы функции
Для начала определимся с терминами:
- параметр — это то, что запрашивает функция при вызове (например,
bins, количество интервалов) - аргумент — значение этого параметра (в нашем случае,
10).
Возникает вопрос, что же такое
height? Логично предположить, что это аргумент (ведь это наши данные). Но тогда как функция узнает, какому параметру он соответствует?
Все дело в том, что параметры и их аргументы могут быть позиционными (positional) и именованными (keyword).
В первом случае, достаточно указать аргумент и поставить его в правильном порядке (позиции). Функция сама поймет, какой параметр ей передают. Во втором случае, нужно указать и название параметра, и аргумент.
Позиционный параметр можно сделать именованным, и тогда порядок важен не будет.
# данные в этой функции обозначаются через x plt.hist(bins = 10, x = height) plt.show() |
Кроме того, функция может иметь параметры с аргументами по умолчанию. Это делает такой параметр не обязательным, а значит упрощает и ускоряет вызов функции.
# у параметра bins есть аргумент по умолчанию (как раз 10 интервалов) # а значит, этот параметр можно не указывать plt.hist(height) plt.show() |
Как вы видите, результат во всех трех случаях совершенно одинаковый.
Если вы сомневаетесь в том, какие параметры принимает функция и что является результатом ее работы, полезно обратиться к документации в Интернете. Например, по функции plt.hist() ее можно найти вот здесь⧉.
Стоит отметить, что функция может как принимать один, два или несколько параметров, так и не принимать их вовсе. Например, функция print(), если не указывать параметры, выдает пустую строку.
print(‘Первая строка’) print() print(‘Третья строка’) |
Первая строка Третья строка |
Функции и методы
Некоторые функции называются методами. Методы — это функции, которые можно применить только к конкретному объекту. Другими словами, если обычная функция будет выполнена «сама по себе», это просто участок кода, которому дали имя, то методу для исполнения нужен объект (например, строка, список или словарь). При этом, что важно, у каждого объекта свои методы.
Предположим, у нас есть строка, и мы хотим сделать первую букву каждого слова заглавной. Для этого у строки есть метод .title().
# создаем строковый объект и some_string = ‘machine learning’ # применяем к нему метод .title() some_string.title() |
А теперь попробуем применить этот метод к списку, состоящему из тех же слов, что и строка.
some_list = [‘machine’, ‘learning’] some_list.title() |
Как мы видим, Питон выдал ошибку.
Собственные функции в Питоне
Объявление и вызов функции
Функции не обязательно должны быть встроены в базовый функционал или библиотеки. Мы вполне можем объявлять (т.е. создавать) собственные функции (user-defined functions). Рассмотрим пример.
# создадим функцию, которая удваивает # любое передаваемое ей значение def double(x): res = x * 2 return res |
# и вызовем ее, передав число 2 double(2) |
Теперь давайте разберем каждый элемент этого кода. Вначале посмотрим как объявить функцию (declare a function).
- ключевое слово def необходимо для объявления функции
- далее идут название функции, которое вы сами определяете, и
- параметры, которые может принимать ваша функция
- после двоеточия на новой строке с отступом идет так называемое тело функции, то есть то, что будет исполняться при вызове функции
- в конце ставится ключевое слово return, возвращающее результат работы функции
Затем, когда это необходимо, мы можем вызвать функцию (call a function), указав ее имя и передав, при их наличии, соответствующие аргументы внутри круглых скобок.
Пустое тело функции
Оставлять тело функции совсем пустым нельзя. Нужно как минимум указать ключевое слово return или оператор pass.
# тело функции не может быть пустым def only_return(): # нужно либо указать ключевое слово return return |
# либо оператор pass def only_pass(): pass |
Хотя может показаться, что в этом случае функция ничего не выдает, на самом деле это не так. И в том, и в другом случае функция возвращает особый тип данных None (отсутствие значения).
Функция print() вместо return
Помимо ключевого слова return, результат работы функции можно вывести с помощью print().
def double_print(x): res = x * 2 print(res) |
Хотя визуально вывод идентичен, отличие все-таки есть:
- Использование return возвращает значение функции (в нашем случае значение переменной res) и прерывает ее работу
- Функция print() просто выводит это значение пользователю и не влияет на дальнейшее исполнение кода, если он есть
Параметры собственных функций
С точки зрения параметров, у собственных функций те же самые возможности, что и у встроенных функций. В частности, параметры могут быть позиционными и именованными.
# объявим функцию с параметрами x и y, def calc_sum(x, y): # которая возвращает их сумму return x + y |
# вызовем эту функцию с одним позиционным и одним именованным параметром calc_sum(1, y = 2) |
Параметры собственной функции также могут быть заданы по умолчанию. В этом случае при вызове функции их указывать не обязательно.
def calc_sum_default(x = 1, y = 2): return x + y calc_sum_default() |
И конечно функция может изначально не иметь параметров.
# эта функция просто выводит текст ‘Some string’ def print_string(): print(‘Some string’) print_string() |
Аннотация функции
Аннотация функции (function annotation) позволяет явно прописать тип данных параметров (parameter annotation) и возвращаемых значений (return annotation).
# укажем, что на входе функция принимает тип float, а возвращает int # значение 3,5 – это значение параметра x по умолчанию def f(x: float = 3.5) -> int: return int(x) |
# желаемый тип данных можно посмотреть через атрибут __annotations__ f.__annotations__ |
{‘return’: int, ‘x’: float} |
# вызовем функцию без параметров f() |
Аннотация не является обязательной и никак не вляет на выполнение кода.
# сохраним аннотации, но изменим суть функции def f(x: float) -> int: # теперь вместо int она будет возвращать float return float(x) |
# вновь вызовем функцию, передав ей на входе int, и ожидая на выходе получить float f(3) |
Дополнительные возможности функций
Вызов функции можно совмещать с арифметическими операциями.
# вызовем объявленную выше функцию и умножим ее вывод на два calc_sum(1, 2) * 2 |
Доступны и логические операции.
Если результатом вывода является строка, то у этой строки также есть индекс.
def first_letter(): return ‘Python’ # обратимся к первой букве слова Python first_letter()[0] |
Функция может не использовать параметры, но получать данные от пользователя через input().
def use_input(): # запросим у пользователя число и переведем его в тип данных int user_inp = int(input(‘Введите число: ‘)) # возведем число в квадрат result = user_inp ** 2 # вернем результат return result # вызовем функцию use_input() |
Появится окно для ввода числа.
Введем число пять и посмотрим на результат.
Результат вызова функции
Функция не обязательно возвращает только число или строку, это может быть, в частности, список, кортеж или словарь. Рассмотрим несколько примеров.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# объявим функцию, которая на входе получает число, # а на выходе формирует список чисел от 0 до числа, # предшествующего заданному def create_list(x): # создадим пустой список l = [] # в цикле for создадим последовательность for i in range(x): # и поместим ее в список l.append(i) return l # результатом вызова этой функции будет список create_list(5) |
Функция может возвращать сразу несколько значений.
def tuple_f(): string = ‘Python’ x = 42 return string, x |
Если результат вызова этой функции записать в две переменных, то ожидаемо в них окажется строка и число.
a, b = tuple_f() print(a, b) print(type(a), type(b)) |
Python 42 <class ‘str’> <class ‘int’> |
Если записать в одну, то получится кортеж.
c = tuple_f() print(c) print(type(c)) |
(‘Python’, 42) <class ‘tuple’> |
Функция может возвращать и логическое значение. Давайте объявим функцию, которая проверяет четное ли ей передали число (и в этом случае вернет True) или нечетное (и тогда False).
# проверим равен ли нулю остаток от деления на два def if_divisible(x): if x % 2 == 0: return True else: return False # попробуем с числом 10 if_divisible(10) |
Использование библиотек
Внутри функций можно использовать дополнительные библиотеки Питона. Например, применим функцию mean() библиотеки Numpy для расчета среднего арифметического.
# на входе функция примет список или массив x, def mean_f(x): # рассчитает среднее арифметическое и прибавит единицу return np.mean(x) + 1 |
# перед вызовом функции нужно не забыть импортировать соответствующую библиотеку import numpy as np # и подготовить данные x = [1, 2, 3] mean_f(x) |
Глобальные и локальные переменные
Некоторые переменные существуют (или как еще говорят, видны) только внутри функции, другие во всей программе. В первом случае, говорят про локальные переменные, во втором — про глобальные. Сразу приведем несложный пример.
# создадим глобальную переменную ВНЕ функции global_name = ‘Петр’ # а затем используем ее внутри новой функции def show_name(): print(global_name) |
Здесь никаких проблем не возникло. Мы создали глобальную переменную и потом легко использовали ее внутри очередной функции. Обратное не всегда возможно.
# а теперь вначале создадим функцию, # внутри которой объявим локальную переменную def show_local_name(): local_name = ‘Алена’ print(local_name) |
Функция была вызвана без проблем. При этом если мы попробуем обратиться к переменной local_name вне этой функции, Питон выдаст ошибку.
Все дело в том, что область видимости этой переменной ограничена функцией show_local_name(). Для того чтобы она была видна во всей программе, можно использовать ключевое слово global.
def make_global(): global local_name local_name = ‘Алена’ print(local_name) |
# теперь ошибки быть не должно local_name |
Давайте посмотрим, что будет, если есть две переменные с одинаковым названием, причем одна из них глобальная, а вторая — локальная.
# объявим глобальную переменную global_number = 5 def print_number(): # затем объявим локальную переменную local_number = 10 print(‘Local number:’, local_number) |
Функция всегда «предпочтет» содержащуюся в ней локальную переменную.
При этом значение глобальной переменной для остального кода не изменится.
print(‘Global number:’, global_number) |
Анонимные или lambda-функции
Функции создают не только через ключевое слово def и название функции. Можно использовать слово lambda и вообще обойтись без названия. Приведем простой пример.
# создадим функцию, которая принимает два числа и перемножает их lf = lambda a, b: a * b # вызовем функцию и передадим ей числа 2 и 3 lf(2, 3) |
Теперь давайте в деталях рассмотрим, как мы получили такой результат.
- вначале ставится ключевое слово lambda
- за ним идут передаваемые параметры
- через двоеточие пишется исполняемое выражение
Разумеется, ничто не мешает поместить этот же функционал в обычную функцию.
def normal_f(a, b): return a * b normal_f(2, 3) |
Анонимную функции при этом удобно использовать в тех случаях, когда применение обычной функции избыточно. Продемонстрирую на примере.
Lambda-функция внутри функции filter()
Предположим, у нас есть список чисел, и мы хотим оставить в нем только те числа, которые больше 10. Давайте решим эту задачу с помощью функции filter() и lambda-функции.
Функция filter() принимает два параметра:
- Во-первых, еще одну функцию, выполняющую роль критерия; она выдает
True, если элемент нужно оставить, и
False — если убрать - Во-вторых, набор элементов, которые нужно отфильтровать в виде списка, кортежа или множества
Получив их, filter() применяет критерий (функцию) к каждому из элементов набора.
Посмотрим на реализацию на Питоне. Вначале создадим список.
nums = [15, 27, 9, 18, 3, 1, 4] |
Теперь зададим фильтрующую lambda-функцию.
# буквально мы пишем, для каждого n, # выдай True, если число больше 10, иначе – False criterion = lambda n: True if (n > 10) else False |
После этого поместим criterion и nums в функцию filter(). Так как сама функция filter() вернет нам не список, а специальный объект iterator, его в свою очередь нужно преобразовать обратно в список с помощью функции list().
list(filter(criterion, nums)) |
Чаще такой функционал записывают в одну строчку.
list(filter(lambda n: True if (n > 10) else False, nums)) |
И в этом и заключается удобство lambda-функции, ее не надо объявлять заранее. Через обычную функцию код выглядел бы так.
def criterion_2(n): if n > 10: return True else: return False list(filter(criterion_2, nums)) |
Lambda-функция внутри функции sorted()
Теперь рассмотрим lambda-функцию в коде, который мы использовали на занятии по рекомендательным системам. Воспроизведем в миниатюре часть решения из того занятия.
В какой-то момент мы получили список из кортежей. И в каждом кортеже был индекс фильма и расстояние до него.
list_of_tuples = [(901, 0.0), (1002, 0.22982440568634488), (442, 0.25401128310081567)] |
Затем мы захотели отсортировать этот список по расстоянию, то есть по второму элементу кортежа.
Мы взяли функцию sorted() и в качестве параметра ключа, по которому сортировать список (key), передали ей lambda-функцию, принимающей кортеж из двух элементов на входе, и выдающей второй его элемент на выходе.
sorted(indices_distances, key = lambda x: x[1], reverse = True) |
[(442, 0.25401128310081567), (1002, 0.22982440568634488), (901, 0.0)] |
Параметр
reverse = True, напомню, задает сортировку по убыванию.
Немедленно вызываемые функции
Lambda-функции относятся к так называемым немедленно вызываемым функциям или immediately invoked function expressions (IIFE). Это означает, что мы можем одновременно объявить и вызвать такую функцию.
# обратите внимание на использование скобок (lambda x: x * x)(10) |
*args и **kwargs
Прежде чем завершить, поговорим про еще одну важную тему, а именно про так называемые *args (сокращение от arguments) и **kwargs (keyword arguments).
Они позволяют передавать функции различное количество позиционных (*args) или именованных (**kwargs) аргументов.
Рассмотрим на примере. Начнем с *args.
*args
Предположим, что у нас есть простая функция, которая принимает два числа и считает среднее арифметическое.
# объявим функцию def mean(a, b): return (a + b) / 2 # и передадим ей числа 1 и 2 mean(1, 2) |
Все отлично работает, но мы не можем передать этой функции больше двух чисел. Возможным решением станет функция, которая изначально принимает список в качестве аргумента.
# объявим функцию, которой нужно передать список def mean(list_): # зададим переменную для суммы, total = 0 # в цикле сложим все числа из списка for i in list_: total += i # и разделим на количество элементов return total / len(list_) |
# создадим список list_ = [1, 2, 3, 4] # и передадим его в новую функцию mean(list_) |
Все опять же работает, но нам каждый раз нужно создавать список. При попытке передать отдельные числа функция выдаст ошибку.
*args позволяет передавать функции произвольное количество отдельных чисел.
# объявим функцию с *args def mean(*nums): total = 0 for i in nums: total += i return total / len(nums) |
Как вы видите, главным элементом здесь является оператор распаковки * (unpacking operator). Он принимает все передаваемые в функцию числа и формирует из них кортеж.
Затем мы проходимся по элементам этого кортежа, рассчитываем их сумму и делим на количество элементов. Использовать слово args не обязательно, мы назвали наш позиционный аргумент nums.
Если мы по какой-то причине захотим передать функции список, мы можем это сделать.
# передадим в функцию список mean(*list_) |
В этом случае мы передаем название списка со звездочкой *.
Для того чтобы продемонстрировать преобразование чисел в кортеж, напишем вот такую несложную функцию.
def test_type(*nums): print(nums, type(nums)) |
(1, 2, 3, 4) <class ‘tuple’> |
**kwargs
При использовании **kwargs происходит почти то же самое за тем исключением, что мы распаковываем именованные, а не позиционные аргументы. И распаковываем их в словарь, а не в список. Сразу посмотрим на примере.
def f(**kwargs): return kwargs.items() |
# оператор ** примет произвольное количество именованных аргументов # и внутри функции сформирует из них словарь f(a = 1, b = 2) |
dict_items([(‘a’, 1), (‘b’, 2)]) |
Приведем более сложный пример. Напишем функцию, которая на вход примет произвольное количество чисел (позиционный аргумент), преобразует в кортеж (*args) и рассчитает среднее арифметическое (mean) и среднее квадратическое отклонение (standard deviation).
Для каждой из метрик мы дополнительно создадим именованный параметр, который определит выводить эту метрику или нет. Параметры мы передадим через **kwargs. Внутри функции из них будет сформирован словарь.
# nums функция преобразует в кортеж, params – в словарь def simple_stats(*nums, **params): # если ключ ‘mean’ есть в словаре params и его значение == True if ‘mean’ in params and params[‘mean’] == True: # рассчитаем среднее арифметическое кортежа nums и округлим # t – это символ табуляции print(f‘mean: t{np.round(np.mean(nums), 3)}’) # если ключ ‘std’ есть в словаре params и его значение == True if ‘std’ in params and params[‘std’] == True: # рассчитаем СКО кортежа nums и округлим print(f‘std: t{np.round(np.std(nums), 3)}’) |
Вызовем функцию simple_stats() и передадим ей числа и именованные аргументы.
simple_stats(5, 10, 15, 20, mean = True, std = True) |
Если для одного из параметров задать значение False, функция не выведет соответствующую метрику.
simple_stats(5, 10, 15, 20, mean = True, std = False) |
Для того чтобы передать параметры списком и словарем, нам нужно использовать операторы распаковки
* и
** соответственно.
list_ = [5, 10, 15, 20] settings = {‘mean’ : True, ‘std’ : True} simple_stats(*list_, **settings) |
Количество именованных аргументов в **kwargs может быть любым. Ничто не мешает нам добавить еще один параметр.
# добавим параметр median simple_stats(5, 10, 15, 20, mean = True, std = True, median = True) |
Впрочем, для того чтобы это имело смысл, такой параметр должен быть прописан внутри функции.
В заключение скажу, что все приведенные выше примеры являются учебными и без *args и **kwargs здесь конечно можно обойтись. На практике, они применяются в более сложных конструкциях, например, в так называемых декораторах, однако эта тема выходит за рамки сегодняшнего занятия.
Подведем итог
Сегодня мы впервые поговорили про функции в программировании и выяснили, чем они отличаются от функций в математике. Кроме того мы узнали, что в Питоне можно:
- использовать готовые функции, которые уже встроены либо в базовый функционал, либо в дополнительную библиотеку;
- объявлять собственные функции через ключевое слово def и название функции; а также
- создавать анонимные или lambda-функции, которые очень удобно применять там, где в полноценных собственных функциях нет необходимости
Помимо этого, мы выяснили, что любой функции можно передать параметры и аргументы, которые в зависимости от способа передачи могут быть позиционными или именованными.
Мы узнали, что у переменной может быть локальная и глобальная области видимости.
Наконец, мы поговорили про возможность передачи различного количества позиционных и именованных аргументов через *args и **kwargs.
Вопросы для закрепления
Какие три вида функций мы изучили?
Посмотреть правильный ответ
Ответ: встроенные, собственные, а также анонимные или lambda-функции.
Какие бывают параметры и аргументы функции?
Посмотреть правильный ответ
Ответ: позиционные (в этом случае мы указываем только аргумент, но ставим его в определенном порядке) и именованные (указываем и параметр, и аргумент, но порядок не важен).
Какова область видимости локальной переменной?
Посмотреть правильный ответ
Ответ: область видимости локальной переменной ограничена той функцией, в которой эта переменная была объявлена.
В ноутбуке к лекции приведены дополнительные упражнения⧉.
В следующий раз мы подробнее рассмотрим списки, кортежи и множества.
Ответы на вопросы
Вопрос. Скажите, а почему используется символ нижнего подчеркивания в названии переменной list_?
Ответ. Не стоит использовать просто list для именования переменных, потому что это слово зарезервировано для названия функции. Аналогично не стоит использовать dict, tuple, set и т.д. При этом некоторые слова использовать в качестве названия переменных просто не получится. Питон выдаст ошибку. Это, например, if, for, True, False, import и т.п.
Символ подчеркивания является одним из вариантов решения.