In any programming language, functions facilitate code reusability. In simple terms, when you want to do something repeatedly, you can define that something as a function and call that function whenever you need to.
In this tutorial, we shall learn about user-defined functions in Python.
When you started coding in Python, you’d have used the built-in print()
function in your Hello World!
program 😀 and the input()
function to read in input from the user.
So long as you know how to use these functions, you don’t have to worry about how they’ve been implemented.
In programming, this is called abstraction. It lets you use functions by calling the function with required arguments, without having to worry about how they actually work.
There’s a whole wealth of built-in functions in Python. In this post, we shall see how we can define and use our own functions. Let’s get started!
Python Function Syntax
The following snippet shows the general syntax to define a function in Python:
def function_name(parameters):
# What the function does goes here
return result
- You need to use the
def
keyword, give your function a name, followed by a pair of parentheses, and end the line with a colon (:). - If your function takes arguments, the names of the arguments (parameters) are mentioned inside the opening and closing parentheses.
- Please note that in function definition, the arguments that your function consumes are referred to as parameters.
- When you call the function with specific values for these parameters, they’re called arguments or actual parameters. This is because the arguments in the function call are the values used for the function’s parameters.
- Then, you begin an indented block. This is the body of the function that describes what your function does.
- There’s a
return
statement that returns the result of the operation on the arguments. Thereturn
statement returns control to the point where the function was originally called.
Note that the arguments
and the return
statement are optional. This means that you could have a function that takes in no arguments, and returns nothing. 😀
Let’s now try to understand the above statements using simple examples.
How to Create a Simple Function in Python
Let’s now create a simple function in Python that greets the user, as shown below:
def my_func():
print("Hello! Hope you're doing well")
As you can see, the function my_func()
:
- takes no arguments,
- returns nothing, and
- prints out
"Hello! Hope you're doing well"
whenever it’s called.
Note that the above function definition is inert until the function is triggered or called. Let’s go ahead and call the function my_func()
and check the output.
my_func()
# Output
Hello! Hope you're doing well
How to Create a Function with Arguments in Python
Now, we shall modify the function my_func()
to include the name
and place
of the user.
def my_func(name,place):
print(f"Hello {name}! Are you from {place}?")
We can now call my_func()
by passing in two strings for the name
and place
of the user, as shown below.
my_func("Jane","Paris")
# Output
Hello Jane! Are you from Paris?
What happens if you specify the place
first and then the name
? Let’s find out.
my_func("Hawaii","Robert")
# Output
Hello Hawaii! Are you from Robert?
We get Hello Hawaii! Are you from Robert?
– and this doesn’t make sense. 🙂What’s causing this problem?
The arguments in the function call are positional arguments. This means that the first argument in the function call is used as the value of the first parameter (name
) and the second argument in the function call is used as the value of the second parameter ( place
)
See the code snippet below. Instead of specifying only the arguments, we’ve mentioned the parameters and the values they take.
my_func(place="Hawaii",name="Robert")
# Output
Hello Robert! Are you from Hawaii?
These are called keyword arguments. The order of arguments in the function call does not matter so long as the names of the parameters are correct.
How to Create a Function with Default Arguments in Python
What if we had certain parameters that take a specific value most of the time during the function calls?
Can we not do better than calling the function with the same value for a particular parameter?
Yes we can do better, and that’s what default arguments
are for! 😀
Let’s create a function total_calc()
that helps us calculate and print out the total amount to be paid at a restaurant. Given a bill_amount
and the percentage of the bill_amount
you choose to pay as tip (tip_perc
), this function calculates the total amount that you should pay.
Note how the function definition includes the default value of the parameter tip_perc
to be used when the user doesn’t specify a tip percentage.
Run the code snippet below.👇🏽 You now have your function ready!
def total_calc(bill_amount,tip_perc=10):
total = bill_amount*(1 + 0.01*tip_perc)
total = round(total,2)
print(f"Please pay ${total}")
Let’s now call the function in a few different ways. The code snippet below shows the following:
- When you call the function
total_calc
with only thebill_amount
, by default the tip percentage of 10 is used. - When you explicitly specify the percentage tip, the tip percentage mentioned in the function call is used.
# specify only bill_amount
# default value of tip percentage is used
total_calc(150)
# Output
Please pay $165.0
# specify both bill_amount and a custom tip percentage
# the tip percentage specified in the function call is used
total_calc(200,15)
# Output
Please pay $230.0
total_calc(167,7.5)
# Output
Please pay $179.53
How to Create a Function that Returns a Value in Python
So far, we’ve only created functions that may or may not take arguments and do not return anything. Now, let’s create a simple function that returns the volume of a cuboid given the length
, the width
, and the height
.
def volume_of_cuboid(length,breadth,height):
return length*breadth*height
Recall that the return
keyword returns control to the point where the function was called. The function call is replaced with the return value
from the function.
Let’s call the function volume_of_cuboid()
with the necessary dimension arguments, as shown in the code snippet below. Note how we use the variable volume
to capture the value returned from the function.
volume = volume_of_cuboid(5.5,20,6)
print(f"Volume of the cuboid is {volume} cubic units")
# Output
Volume of the cuboid is 660.0 cubic units
How to Create a Function that Returns Multiple Values in Python
In our earlier example, the function volume_of_cuboid()
returned only one value, namely, the volume of a cuboid given its dimensions. Let’s see how we can return multiple values from a function.
- To return multiple values from a function, just specify the values to be returned, separated by a comma.
- By default, the function returns the values as a tuple. If there are
N
return values, we get anN-
tuple.
Let’s create a simple function cube()
that returns the volume and total surface area of a cube, given the length of its side.
def cube(side):
volume = side **3
surface_area = 6 * (side**2)
return volume, surface_area
To verify that a tuple is returned, let’s collect it in a variable returned_values
, as shown below:
returned_values = cube(8)
print(returned_values)
# Output
(512, 384)
Now, we shall unpack the tuple and store the values in two different variables.
volume, area = cube(6.5)
print(f"Volume of the cube is {volume} cubic units and the total surface area is {area} sq. units")
# Outputs
Volume of the cube is 274.625 cubic units and the total surface area is 253.5 sq. units
How to Create a Function that Takes a Variable Number of Arguments in Python
Let’s start by asking a few questions:
- What if we do not know the exact number of arguments beforehand?
- Can we create functions that work with a variable number of arguments?
The answer is yes! And we’ll create such a function right away.
Let’s create a simple function my_var_sum()
that returns the sum of all numbers passed in as the argument. However, the number of arguments could be potentially different each time we call the function.
Notice how the function definition now has *args
instead of just the name of the parameter. In the body of the function, we loop through args
until we’ve used all the arguments. The function my_var_sum
returns the sum of all numbers passed in as arguments.
def my_var_sum(*args):
sum = 0
for arg in args:
sum += arg
return sum
Let’s now call the function my_var_sum()
with a different number of arguments each time and quickly check if the returned answers are correct! 🙂
# Example 1 with 4 numbers
sum = my_var_sum(99,10,54,23)
print(f"The numbers that you have add up to {sum}")
# Output
The numbers that you have add up to 186
# Example 2 with 3 numbers
sum = my_var_sum(9,87,45)
print(f"The numbers that you have add up to {sum}")
# Output
The numbers that you have add up to 141
# Example 3 with 6 numbers
sum = my_var_sum(5,21,36,79,45,65)
print(f"The numbers that you have add up to {sum}")
# Output
The numbers that you have add up to 251
⌛ A Quick Recap
Let’s quickly summarize what we’ve covered. In this tutorial, we’ve learned:
- how to define functions,
- how to pass in arguments to a function,
- how to create functions with default and variable number of arguments, and
- how to create a function with return value(s).
Hope you all enjoyed reading this article. Thank you for reading. As always, until next time! 😀
Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started
Функция это блок организованного, многократно используемоего кода, который используется для выполнения конкретного задания. Функции обеспечивают лучшую модульность приложения и значительно повышают уровень повторного использования кода.
Создание функции
Существуют некоторые правила для создания функций в Python.
- Блок функции начинается с ключевого слова def, после которого следуют название функции и круглые скобки ( () ).
- Любые аргументы, которые принимает функция должны находиться внутри этих скобок.
- После скобок идет двоеточие ( : ) и с новой строки с отступом начинается тело функции.
Пример функции в Python:
def my_function(argument): print argument
Вызов функции
После создания функции, ее можно исполнять вызывая из другой функции или напрямую из оболочки Python. Для вызова функции следует ввести ее имя и добавить скобки.
Например:
my_function("abracadabra")
Вызывая функцию, мы можем передавать ей следующие типы аргументов:
- Обязательные аргументы (Required arguments)
- Аргументы-ключевые слова (Keyword argument)
- Аргументы по умолчанию (Default argument)
- Аргументы произвольной длины (Variable-length argumens)
Обязательные аргументы функции:
Если при создании функции мы указали количество передаваемых ей аргументов и их порядок, то и вызывать ее мы должны с тем же количеством аргументов, заданных в нужном порядке.
Например:
def bigger(a,b): if a > b: print a else: print b # В описании функции указано, что она принимает 2 аргумента # Корректное использование функции bigger(5,6) # Некорректное использование функции bigger() bigger(3) bigger(12,7,3)
Аргументы – ключевые слова
Аргументы – ключевые слова используются при вызове функции. Благодаря ключевым аргументам, вы можете задавать произвольный (то есть не такой каким он описа при создании функции) порядок аргументов.
Например:
def person(name, age): print name, "is", age, "years old" # Хотя в описании функции первым аргументом идет имя, мы можем вызвать функцию вот так person(age=23, name="John")
Аргументы, заданные по-умолчанию
Аргумент по умолчанию, это аргумент, значение для которого задано изначально, при создании функции.
Например:
def space(planet_name, center="Star"): print planet_name, "is orbiting a", center # Можно вызвать функцию space так: space("Mars") # В результате получим: Mars is orbiting a Star # Можно вызвать функцию space иначе: space("Mars", "Black Hole") # В результате получим: Mars is orbiting a Black Hole
Аргументы произвольной длины
Иногда возникает ситуация, когда вы заранее не знаете, какое количество аргументов будет необходимо принять функции. В этом случае следует использовать аргументы произвольной длины. Они задаются произвольным именем переменной, перед которой ставится звездочка (*).
Например:
def unknown(*args): for argument in args: print argument unknown("hello","world") # напечатает оба слова, каждое с новой строки unknown(1,2,3,4,5) # напечатает все числа, каждое с новой строки unknown() # ничего не выведет
Ключевое слово return
Выражение return прекращает выполнение функции и возвращает указанное после выражения значение. Выражение return без аргументов это то же самое, что и выражение return None. Соответственно, теперь становится возможным, например, присваивать результат выполнения функции какой либо переменной.
Например:
def bigger(a,b): if a > b: return a # Если a больше чем b, то возвращаем a и прекращаем выполнение функции return b # Незачем использовать else. Если мы дошли до этой строки, то b, точно не меньше чем a # присваиваем результат функции bigger переменной num num = bigger(23,42)
Область видимости
Некоторые переменные скрипта могут быть недоступны некоторым областям программы. Все зависит от того, где вы объявили эти переменные.
В Python две базовых области видимости переменных:
- Глобальные переменные
- Локальные переменные
Переменные объявленные внутри тела функции имеют локальную область видимости, те что объявлены вне какой-либо функции имеют глобальную область видимости.
Это означает, что доступ к локальным переменным имеют только те функции, в которых они были объявлены, в то время как доступ к глобальным переменным можно получить по всей программе в любой функции.
Например:
# глобальная переменная age age = 44 def info(): print age # Печатаем глобальную переменную age def local_info(): age = 22 # создаем локальную переменную age print age info() # напечатает 44 local_info() # напечатает 22
Важно помнить, что для того чтобы получить доступ к глобальной переменной, достаточно лишь указать ее имя. Однако, если перед нами стоит задача изменить глобальную переменную внутри функции – необходимо использовать ключевое слово global.
Например:
# глобальная переменная age age = 13 # функция изменяющая глобальную переменную def get_older(): global age age += 1 print age # напечатает 13 get_older() # увеличиваем age на 1 print age # напечатает 14
Рекурсия
Рекурсией в программировании называется ситуация, в которой функция вызывает саму себя. Классическим примером рекурсии может послужить функция вычисления факториала числа.
Напомним, что факториалом числа, например, 5 является произведение всех натуральных (целых) чисел от 1 до 5. То есть, 1 * 2 * 3 * 4 * 5
Рекурсивная функция вычисления факториала на языке Python будет выглядеть так:
def fact(num): if num == 0: return 1 # По договоренности факториал нуля равен единице else: return num * fact(num - 1) # возвращаем результат произведения num и результата возвращенного функцией fact(num - 1)
Однако следует помнить, что использование рекурсии часто может быть неоправданным. Дело в том, что в момент вызова функции в оперативной памяти компьютера резервируется определенное количество памяти, соответственно чем больше функций одновременно мы запускаем – тем больше памяти потребуется, что может привести к переполнению стека (stack overflow) и программа завершится аварийно, не так как предполагалось. Учитывая это, там где это возможно, вместо рекурсии лучше применять циклы.
Рецепт создания функции в Python
Существует следующий алгоритм – рекомендация по созданию функции в Python. Например, мы создаем функцию вычисления площади прямоугольника.
- Начинать следует с примеров того, что делает функция, и подобрать подходящее название. В нашем случае это будет выглядеть так:
# На данном этапе мы еще не указываем имена переменных def rectangle_area_finder( ): """ >>> rectangle_area_finder(3, 5) 15 >>> rectangle_area_finder(17.2, 6) 103.2 """
- Указать типы данных, которые принимает функция и тип данных, который она возвращает
# функция принимает два числа, а возвращает одно def rectangle_area_finder( ): """ (num, num) -> num >>> rectangle_area_finder(3, 5) 15 >>> rectangle_area_finder(17.2, 6) 103.2 """
- Подобрать подходящие названия для переменных
# Поскольку это математическая функция нам вполне подойдут имена a и b def rectangle_area_finder(a, b): """ (num, num) -> num >>> rectangle_area_finder(3, 5) 15 >>> rectangle_area_finder(17.2, 6) 103.2 """
- Написать краткое, но содержательное описание функции
def rectangle_area_finder(a, b): """ (num, num) -> num Returns an area of a rectangle with given sides a and b. >>> rectangle_area_finder(3, 5) 15 >>> rectangle_area_finder(17.2, 6) 103.2 """
- Написать собственно тело функции
def rectangle_area_finder(a, b): """ (num, num) -> num Returns an area of a rectangle with given sides a and b. >>> rectangle_area_finder(3, 5) 15 >>> rectangle_area_finder(17.2, 6) 103.2 """ return a * b
- Функция готова! Осталось вызвать ее с указанными в примерах аргументами
Как видно, при вызове команды help() с именем нашей функции в качестве аргумента мы получаем написанную нами документацию.
Сопровождайте ваши функции качественной документацией и программисты, которые будут работать с вашим кодом после вас будут вам благодарны.
Функция – это мини-программа внутри основной программы. Код
такой подпрограммы отвечает за решение определенной задачи: например, в игре
Тетрис будут отдельные функции для подсчета очков, рисования игрового поля, движения
фигурки и так далее. Использование функций позволяет:
- ограничить область видимости переменных функциями, которые их используют;
- исключить дублирование кода;
- разбить большую и сложную программу на небольшие мини-программы, которые можно вызывать в нужный момент;
- выстроить простую и понятную структуру программы – такой код удобнее дебажить и поддерживать.
У функций есть несколько особенностей:
- Функция выполняется только тогда, когда ее вызывает основная программа.
- В функцию можно передавать различные данные. Параметры – это переменные, которые используются при объявлении функции, аргументы – фактические значения, которые передаются переменным при вызове функции.
- Функции могут передавать результаты своей работы в основную программу или в другие функции.
Python работает со встроенными и пользовательскими функциями. Встроенные функции – это уже знакомые нам print(), input(), map(), zip() и так далее. Пользовательские функции, в свою очередь, делятся на:
- Рекурсивные (вызывают сами себя до тех пор, пока не будет достигнут нужный результат).
- Анонимные, или лямбда-функции (объявляются в любом участке кода и сразу же вызываются).
- Все остальные функции, которые определены пользователем и не относятся ни к рекурсивным, ни к анонимным.
В этой статье мы рассмотрим пользовательские функции с различными типами параметров, а в последующих статьях разберем анонимные и рекурсивные функции.
Объявление и вызов функций в Python
Для создания функции используют ключевое слово def
. Вот пример простейшей функции, которая не получает и не возвращает никаких данных – просто выполняет одну команду по выводу строки с приветствием:
def my_function():
print('Привет от Python')
Для вызова такой функции достаточно написать ее название:
my_function()
Результат вызова:
Привет от Python
А это пример простейшей функции с параметром:
def my_function(name):
print(f'Привет, {name}')
При вызове функция получает аргумент:
my_function('Вася')
Результат вызова:
Привет, Вася
При вызове функция ожидает получить набор значений, соответствующий числу параметров. К примеру, эта функция должна получить при вызове два позиционных аргумента – имя и фамилию:
def my_function(name, lastname):
print(f'Добрый день, {name} {lastname}')
Если передать в функцию два аргумента – my_function('Егор', 'Куликов')
, результат вызова будет таким:
Добрый день, Егор Куликов
Но если число аргументов окажется меньше числа параметров – my_function('Алена')
, возникнет ошибка:
my_function('Алена')
TypeError: my_function() missing 1 required positional argument: 'lastname'
Порядок обработки позиционных аргументов
Python
обрабатывает позиционные аргументы слева направо:
def my_function(name, last_name, occupation, age):
print(f'Сотрудник #1 - {name} {last_name} {occupation} {age}')
info1, info2, info3, info4 = 'Алиса', 'Селезнева', 'скрам-мастер', 30
my_function(info1, info2, info3, info4)
my_function(info2, info3, info1, info4)
my_function(info4, info1, info2, info3)
Вывод:
Сотрудник #1 - Алиса Селезнева скрам-мастер 30
Сотрудник #1 - Селезнева скрам-мастер Алиса 30
Сотрудник #1 - 30 Алиса Селезнева скрам-мастер
Аргументы по умолчанию
Функция может использовать аргументы по умолчанию – они
указываются после позиционных:
def my_function(strt, build, ap, city='Москва'):
print(f'Адрес: г.{city}, ул.{strt}, д.{build}, кв.{ap}')
my_function('Красная', '5', '3', 'Тула')
my_function('Красная', '5', '3')
Результат:
Адрес: г.Тула, ул.Красная, д.5, кв.3
Адрес: г.Москва, ул.Красная, д.5, кв.3
Именованные аргументы
Помимо позиционных, в функцию можно передать именованные
аргументы, причем порядок передачи именованных аргументов при вызове функции
может не совпадать с порядком параметров:
def sales_price(price, discount=5):
return price - price * discount / 100
print(sales_price(5000))
print(sales_price(5000, discount=10))
print(sales_price(discount=15, price=5000))
Вывод:
4750.0
4500.0
4250.0
Произвольное количество позиционных аргументов *args
До сих пор мы передавали в функцию определенное, заранее известное число позиционных аргументов. Если в функцию нужно передать произвольное количество
аргументов, используют *args
:
def my_function(*args):
print(f'Минимальное число: {min(args)}, максимальное: {max(args)}')
my_function(1, 4, 5, 2, -5, 0, 12, 11)
Результат вызова:
Минимальное число: -5, максимальное: 12
При использовании *args
функция получает кортеж аргументов, и к ним можно обращаться так же, как к элементам кортежа:
def my_function(*args):
print(f'Первое слово: {args[0]}, последнее слово: {args[-1]}')
my_function('яблоко', 'виноград', 'апельсин', 'арбуз', 'слива', 'груша')
Результат вызова:
Первое слово: яблоко, последнее слово: груша
Название набора параметров, *args, используется по умолчанию. При
желании его можно изменить на любое другое название с *
в начале:
def my_function(*cities):
print(f'Первый город: {cities[0]}, третий город: {cities[2]}')
my_function('Тюмень', 'Москва', 'Орел', 'Новгород', 'Ижевск', 'Ульяновск')
Результат вызова:
Первый город: Тюмень, третий город: Орел
Аргументы *args
обрабатываются после позиционных, но до аргументов по умолчанию:
def my_function(x, y, *args, kx=15, ky=15):
print(x, y, args, kx, ky)
my_function(5, 6, 7, 8, 9, 0, 4)
Вывод:
5 6 (7, 8, 9, 0, 4) 15 15
Произвольное количество именованных аргументов **kwargs
Как уже было отмечено выше, именованные аргументы передаются в функцию в виде пар ключ=значение
:
def my_function(cat1, cat2, cat3):
print(f'Младший кот: {cat1}, старший кот: {cat2}')
my_function(cat1='Том', cat2='Барсик', cat3='Полосатик')
Результат вызова:
Младший кот: Том, старший кот: Барсик
В приведенном выше примере количество именованных аргументов известно заранее. Если в функцию нужно передать произвольное количество пар
ключ=значение
, используют параметр **kwargs
. С **kwargs
работают все методы словарей:
def my_function(**kwargs):
print(f'Самый легкий металл - {min(kwargs, key=kwargs.get)} {min(kwargs.values())}, самый тяжелый - {max(kwargs, key=kwargs.get)} {max(kwargs.values())}')
my_function(осмий=22.61, цинк=7.1, золото=19.3, ртуть=13.6, олово=7.3)
Результат вызова:
Самый легкий металл - цинк 7.1, самый тяжелый - осмий 22.61
Как и в случае с *args, название по умолчанию **kwargs при желании можно заменить на любое другое с **
в начале:
def my_function(**countries):
print(f'Самая густонаселенная страна - {max(countries, key=countries.get)} {max(countries.values())} чел/км2, самая малонаселенная - {min(countries, key=countries.get)} {min(countries.values())} чел/км2')
my_function(Мальта=1432, Дания=128, Монако=18679, Индия=357, Монголия=2)
Результат вызова:
Самая густонаселенная страна - Монако 18679 чел/км2, самая малонаселенная - Монголия 2 чел/км2
Аргументы типа **kwargs
обрабатываются после позиционных, *args
и аргументов по умолчанию:
def my_function(x, y, *args, kx=15, ky=15, **kwargs):
print(x, y, args, kx, ky, kwargs)
my_function(7, 8, 0, 3, 4, 1, 8, 9, север=15, запад=25, восток=45, юг=10)
Вывод:
7 8 (0, 3, 4, 1, 8, 9) 15 15 {'север': 15, 'запад': 25, 'восток': 45, 'юг': 10}
Передача аргументов в виде списка
Помимо кортежей и словарей, в функции можно передавать
списки:
def my_function(stationery):
for i, j in enumerate(stationery):
print(f'Товар #{i + 1} - {j}')
stuff = ['карандаш', 'ручка', 'блокнот', 'альбом', 'тетрадь', 'ластик']
my_function(stuff)
Результат вызова:
Товар #1 - карандаш
Товар #2 - ручка
Товар #3 - блокнот
Товар #4 - альбом
Товар #5 - тетрадь
Товар #6 - ластик
Заглушка pass
Тело функции не может быть пустым – это приведет к сообщению
об ошибке:
def my_function():
Вывод:
def my_function():
^
SyntaxError: unexpected EOF while parsing
Если по какой-то причине нужно оставить тело функции пустым, используют оператор pass
, который выступает в роли заглушки:
def my_function():
pass
Функции с возвратом значений
Как уже было показано выше, функции могут получать и
обрабатывать любые типы данных – строки, числа, списки, кортежи, словари.
Результат обработки можно получить с помощью оператора return. Эта функция возвращает
произведение произвольного количества значений:
def my_function(*args):
prod = 1
for i in args:
prod *= i
return prod
print(my_function(5, 6, 3, 11))
Значения передаются в функцию при вызове – print(my_function(5, 6, 3, 11))
. Результат при таком наборе цифр будет равен 990
. Оператор return может возвращать любое количество значений, причем значения возвращаются в виде кортежа:
def calculations(a, b):
summa = a + b
diff = a - b
mul = a * b
div = a / b
return summa, diff, mul, div
num1, num2 = int(input()), int(input())
summa, diff, mul, div = calculations(num1, num2)
print(
f'Сумма: {summa}n'
f'Разница: {diff}n'
f'Произведение: {mul}n'
f'Результат деления: {div:.2f}n'
)
Пример ввода:
49
6
Вывод:
Сумма: 55
Разница: 43
Произведение: 294
Результат деления: 8.17
Функция может содержать любое количество return
. Эта функция возвращает различные оценки индекса массы тела:
def bmi(h, w):
bmi = w / (h / 100) ** 2
if bmi <= 18.5:
return 'У вас дефицит веса'
elif bmi <= 24.9:
return 'Вес в норме'
elif bmi <= 29.9:
return 'Есть лишний вес'
else:
return 'Срочно на диету!'
h = float(input('Введите рост в см: '))
w = float(input('Введите вес в кг: '))
print(bmi(h, w))
Пример ввода:
Введите рост в см: 172
Введите вес в кг: 61
Вывод:
Вес в норме
Однако эту функцию можно переписать так, чтобы использовался только один оператор return
:
def bmi(h, w):
bmi = w / (h / 100) ** 2
if bmi <= 18.5:
res = 'У вас дефицит веса'
elif bmi <= 24.9:
res = 'Вес в норме'
elif bmi <= 29.9:
res = 'Есть лишний вес'
else:
res = 'Срочно на диету!'
return res
h = float(input('Введите рост в см: '))
w = float(input('Введите вес в кг: '))
print(bmi(h, w))
Практика
Задание 1
Напишите функцию для вывода треугольника. Функция принимает
два аргумента – size (размер сторон треугольника) и symb (символ, используемый для заполнения
треугольника).
Пример ввода:
9
.
Вывод:
.
..
...
....
.....
....
...
..
.
Решение:
def draw_triangle(size, symb):
for i in range(1, size + 1):
print(symb * min(i, size - i + 1))
size, symb = int(input()), input()
draw_triangle(size, symb)
Задание 2
Напишите функцию, которая принимает произвольное количество
целых чисел, и возвращает среднее арифметическое без использования встроенных
функции sum()
и len()
.
Пример вызова:
print(arith_mean(5, 5, 15, 25, 35))
Вывод:
17.0
Решение:
def arith_mean(*args):
summa = 0
kol = 0
for i in args:
summa += i
kol += 1
return summa / kol
Задание 3
Напишите функцию, которая:
- принимает строку, состоящую из букв, цифр и специальных символов;
- формирует три списка – 1) из цифр, 2) из букв, 3) из спецсимволов;
- выводит списки на экран.
Пример ввода:
23edwd893rjf934#$%Ye34F^(*))_+W$#Ddq2ddscew3r
Вывод:
2 3 8 9 3 9 3 4 3 4 2 3
e d w d r j f Y e F W D d q d d s c e w r
# $ % ^ ( * ) ) _ + $ #
Решение:
def sort_list(st):
digits = [i for i in st if i.isdigit()]
letters = [i for i in st if i.isalpha()]
spec_char = [i for i in st if not i.isalnum()]
print(*digits)
print(*letters)
print(*spec_char)
my_st = input()
sort_list(my_st)
Задание 4
Напишите функцию, которая начисляет новогодние премии
сотрудникам. Эта функция:
- имеет два аргумента по умолчанию –
salary=120000
иbonus=10
(оклад и премия); - получает два позиционных аргумента
name
иlast_name
– имя и фамилию сотрудника; - учитывает индивидуальные оклад и премию (см. примеры вызова);
- выводит размер новогодней премии для сотрудника и зарплату с учетом премии.
Примеры вызова функции:
ny_bonus('Алина', 'Тимофеева', salary=150000, bonus=25)
ny_bonus('Алексей', 'Ковалев', bonus=15)
ny_bonus('Игорь', 'Ефимов')
ny_bonus('Анастасия', 'Яковлева', salary=100000, bonus=20)
Вывод:
Новогодняя премия сотрудника Алина Тимофеева: 37500.00 руб.
Оклад: 150000.00 руб.
Всего к выдаче: 187500.00 руб.
Новогодняя премия сотрудника Алексей Ковалев: 18000.00 руб.
Оклад: 120000.00 руб.
Всего к выдаче: 138000.00 руб.
Новогодняя премия сотрудника Игорь Ефимов: 12000.00 руб.
Оклад: 120000.00 руб.
Всего к выдаче: 132000.00 руб.
Новогодняя премия сотрудника Анастасия Яковлева: 20000.00 руб.
Оклад: 100000.00 руб.
Всего к выдаче: 120000.00 руб.
Решение:
def ny_bonus(name, last_name, salary=120000, bonus=10):
print(f'Новогодняя премия сотрудника {name} {last_name}: {salary * bonus / 100:.2f} руб.n'
f'Оклад: {salary:.2f} руб.n'
f'Всего к выдаче: {salary + salary * bonus / 100:.2f} руб.n')
Задание 5
Напишите программу, которая выводит Есть
, если в полученной от
пользователя строке есть хотя бы одно совершенное
число, равное сумме своих делителей, и Нет
в обратном случае.
Пример ввода:
5 7 8 9 34 28
Вывод:
Есть
Решение:
def perfect_number(n):
sum = 0
for i in range(1, n):
if n % i == 0:
sum += i
return sum == n
numbers = list(map(int, input().split()))
flag = 'Нет'
for i in numbers:
if perfect_number(i):
flag = 'Есть'
break
print(flag)
Задание 6
Напишите функцию, которая принимает два позиционных
аргумента – натуральные числа n и k,
и возвращает значение биномиального
коэффициента, не используя math.factorial()
.
Пример ввода:
12
5
Вывод:
792
Решение:
def factorial(num):
if num <= 1:
return 1
return num * factorial(num - 1)
def binomial_coeff(n, k):
return int(factorial(n) / (factorial(k) * factorial(n - k)))
n, k = int(input()), int(input())
print(binomial_coeff(n, k))
Задание 7
Напишите функцию, которая принимает число от 1 до 99, и
возвращает его словесное описание.
Пример ввода:
25
Вывод:
двадцать пять
Решение:
def spell_number(num):
ed = ['один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять', 'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать']
des = ['двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто']
if num < 20:
return ed[num - 1]
elif num >= 20:
if str(num)[1] != '0':
return des[int(str(num)[0]) - 2] + ' ' + ed[int(str(num)[1]) - 1]
return des[int(str(num)[0]) - 2]
n = int(input())
print(spell_number(n))
Задание 8
Напишите функцию, которая возвращает True
, если введенная пользователем дата
является магической, и False
в обратном случае. Магической считается дата, в которой
произведение дня и месяца равно двум последним цифрам года: 02.11.2022.
Пример ввода:
03.06.2018
Вывод:
True
Решение:
def magic_date(date):
return int(date[:2]) * int(date[3:5]) == int(date[-2:])
date = input()
print(magic_date(date))
Задание 9
Напишите функцию, которая принимает произвольное количество
именованных аргументов, и формирует из них строку запроса. Аргументы в строке
запроса должны быть отсортированы в алфавитном порядке.
Примеры вызова:
print(make_query(category='books', genre='thriller', author='Stephen_King'))
print(make_query(name='Егор', last_name='Тимохин', age=25, occupation='дизайнер'
Вывод:
author=Stephen_King&category=books&genre=thriller
age=25&last_name=Тимохин&name=Егор&occupation=дизайнер
Решение:
def make_query(**kwargs):
return '&'.join([f'{k}={kwargs[k]}' for k in sorted(kwargs)])
Задание 10
Напишите функцию, которая принимает целое число n, и выводит на экран спиральную матрицу размера n x n, все элементы которой выровнены по левому краю.
Пример ввода:
9
Вывод:
1 2 3 4 5 6 7 8 9
32 33 34 35 36 37 38 39 10
31 56 57 58 59 60 61 40 11
30 55 72 73 74 75 62 41 12
29 54 71 80 81 76 63 42 13
28 53 70 79 78 77 64 43 14
27 52 69 68 67 66 65 44 15
26 51 50 49 48 47 46 45 16
25 24 23 22 21 20 19 18 17
Решение:
def print_matrix(n):
matrix = [[0] * n for i in range(n)]
vx, vy = [0, 1, 0, -1], [1, 0, -1, 0]
x, y, z = 0, -1, 1
for i in range(n + n - 1):
for j in range((n + n - i) // 2):
x += vx[i % 4]
y += vy[i % 4]
matrix[x][y] = z
z += 1
for i in range(len(matrix)):
for j in range(len(matrix[i])):
print(str(matrix[i][j]).ljust(3), end='')
print()
print_matrix(int(input()))
Подведем итоги
В этой статье мы научились создавать пользовательские
функции и передавать в них определенное число позиционных и именованных
аргументов, а также произвольное количество значений *args
и **kwargs
.
В следующей статье будем разбирать анонимные лямбда-функции.
***
Содержание самоучителя
- Особенности, сферы применения, установка, онлайн IDE
- Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
- Типы данных: преобразование и базовые операции
- Методы работы со строками
- Методы работы со списками и списковыми включениями
- Методы работы со словарями и генераторами словарей
- Методы работы с кортежами
- Методы работы со множествами
- Особенности цикла for
- Условный цикл while
- Функции с позиционными и именованными аргументами
- Анонимные функции
- Рекурсивные функции
- Функции высшего порядка, замыкания и декораторы
- Методы работы с файлами и файловой системой
- Регулярные выражения
- Основы скрапинга и парсинга
- Основы ООП: инкапсуляция и наследование
- Основы ООП – абстракция и полиморфизм
- Графический интерфейс на Tkinter
***
Введение
Определение
Вот пример простой функции:
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]
Содержание:развернуть
- Синтаксис
- Термины и определения
- Важность функций
-
Абстракция
-
Возможность повторного использования
-
Модульность
-
Пространство имен
- Объявление и вызов функций
- Область видимости функций
-
Локальная (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
😉