Функции Python

Основы

Одним из главных принципов программирования является принцип DRY – Don’t Repeat Yourself (не повторяйся). Этот принцип гласит: не стоит в разных местах кода использовать одинаковые блоки. Один из способов сделать это — использовать функции. Функция – это часть кода, которой присвоено имя и к этому коду можно обращаться из других частей программы по имени. Python, как всегда, даёт нам удобный инструментарий для реализации функционального программирования.

Определение

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

def capitalaizer(text):
return text.capitalize()+'!'
var = capitalaizer('hello world')
print(var)
# Вывод:

Hello world!


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

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

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

def capitalaizer(text):
print('Печатаем внутри функции:', end=' ')
print(text.capitalize()+'!')
var = capitalaizer('hello world')
print('Печатаем значение, возвращаемое функцией:', end=' ')
print(var)
print('и его тип:', end=' ')
print(type(var))
# Вывод:

Печатаем внутри функции: Hello world!

Печатаем значение, возвращаемое функцией: None

и его тип: <class 'NoneType'>


Как мы и говорили, функция вернула None, а модифицированная строка распечатана внутри функции. Такие функции называются «не чистыми» и часто являются признаком плохого кода.

Вызовы

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


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

Особенности

Если функция очень короткая, то Пайтон позволяет записать её в одну строку:

def capitalaizer(text): return text.capitalize()+'!'


Но злоупотреблять таким стилем программирования не стоит. В среднем, работа программиста состоит на 10% из написания и на 90% из чтения своего и чужого кода. Одной из основ философии Питона является то, что код должен быть максимально удобочитаемым.
Да, любители писать всё в одну строку чаще всего сами создают себе сложности. Но когда же стоит использовать такой стиль? У меня есть однозначный ответ: при работе с консолью. Именно при работе в Python Shell приходится писать много кода вручную, и Вы его, скорее всего, не будете перечитывать. Именно поэтому языки, разработанные специально для консоли (Bash, PowerShell, .bat), так лаконичны. Посмотрите на некоторые из их команд: cd .. ls -l.
Функции могут быть вложенными:

def capitalaizer(text):
def add_volume(cap_text):
return cap_text + '!'
return add_volume(text.capitalize())
var = capitalaizer('hello world')
print(var)
# Вывод:

Hello world!


Иногда без этого не обойтись, но лучше, по возможности, не использовать дополнительную вложенность – она усложняет код.

Инструкция return

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

В среде программистов Вы можете часто встретить словосочетание «возвращает значение», но функция его ни откуда не заимствовала – почему же она его «возвращает»?

Чтобы это понять, надо разобраться, как устроен поток выполнения программы. В Пайтоне код выполняется строка за строкой. Но, когда в строке встречается имя функции, поток выполнения переходит в тело функции (вспомните, в самом начале мы говорили, что функция – это именованная область кода). После выполнения тела функции, поток выполнения возвращается в то место, откуда была вызвана функция. При этом функция передаёт какое-то значение. Так что «функция возвращает значение» — лишь сокращение от «функция возвращает управление основному потоку выполнения программы и передаёт значение».

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

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

def capitalaizer(text):
return text.capitalize(), '!'
var = capitalaizer('hello world')
print(var)
# Вывод:

('Hello world', '!')


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

def capitalaizer(text):
return text.capitalize(), '!'
var = capitalaizer('hello world')
print(type(var))
# Вывод:

<class 'tuple'>


Что ж, в Python есть замечательный инструмент – распаковка:

def capitalaizer(text):
return text.capitalize(), '!'
var_1, var_2 = capitalaizer('hello world')
print('type(var_1):', type(var_1))
print('type(var_2):', type(var_2))
print('var_1 value:', var_1)
print('var_2 value:', var_2)
# Вывод:

type(var_1): <class 'str'>

type(var_2): <class 'str'>

var_1 value: Hello world

var_2 value: !

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

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

Разбирая примеры выше мы сознательно умолчали о параметрах (аргументах) функции, но сейчас пришло время поговорить и о них. После ключевого слова def и имени capitalaizer мы указали в круглых скобках переменную text. В круглых скобках указываются параметры, которые принимает функция. Если функция не принимает параметры, то скобки всё равно необходимо записать, но оставить их пустыми, иначе:

def qwerty:
pass
# Вывод:

File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_1.py", line 11

def qwerty:

^

SyntaxError: invalid syntax

 

Process finished with exit code 1


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

def example(a, b):
return a - b
print('example(1, 2):', example(1, 2))
print('example(2, 1):', example(2, 1))
# Вывод:

example(1, 2): -1

example(2, 1): 1


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

def example(a=3, b=2):
return a - b
print('example():', example())
# Вывод:

example(): 1


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

def example(a=3, b):
return a - b
print('example():', example())
# Вывод:

File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_1.py", line 11

def example(a=3, b):

^

SyntaxError: non-default argument follows default argument

 

Process finished with exit code 1


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

def example(a, b=1):
return a - b
print('example(2):', example(2))
# Вывод:

example(2): 1

print('example(2, 3):', example(2, 3))
# Вывод:

example(2, 3): -1

print('example(b=2, 3):', example(b=2, 3))
# Вывод:

File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_1.py", line 3

print('example(b=2, 3):', example(b=2, 3))

^

SyntaxError: positional argument follows keyword argument

 

Process finished with exit code 1

print('example(b=2, a=3):', example(b=2, a=3))
# Вывод:

example(b=2, a=3): 1


Mожно так же использовать словарь с распаковкой:

def example(a, b=1):
return a - b

var_dict = {'b': 2, 'a': 3}
print('example(**var_dict):', example(**var_dict))
# Вывод:

example(**var_dict): 1


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

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

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

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

def capitalaizer(*texts):
return [text.capitalize() for text in texts]


var = capitalaizer('hello', 'world')
print(var)
# Вывод:

['Hello', 'World']


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

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

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

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

def capitalaizer(**texts):
return [text.capitalize() for text in texts.values()]


var = capitalaizer(var_1='hello', var_2='world')
print(var)
# Вывод:

['Hello', 'World']


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

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

Хорошей практикой является документирование функций при помощи docstrings:

def capitalaizer(text):
"""
Принимает строку и возвращает её же,
но начинающуюся с заглавной буквы
"""
return text.capitalize()


var = capitalaizer('hello world')
print(var)
# Вывод:

Hello world


Получить доступ к докстрингс можно с помощью приватного дандер-метода __doc__, но лучше использовать функцию help().

def capitalaizer(text):
"""
Принимает строку и возвращает её же,
но начинающуюся с заглавной буквы
"""
return text.capitalize()


print('capitalaizer.__doc__:')
print(capitalaizer.__doc__)
print('help(capitalaizer):')
print(help(capitalaizer))
# Вывод:

capitalaizer.__doc__:

 

Принимает строку и возвращает её же,

но начинающуюся с заглавной буквы

 

help(capitalaizer):

Help on function capitalaizer in module __main__:

 

capitalaizer(text)

Принимает строку и возвращает её же,

но начинающуюся с заглавной буквы

 

None

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

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

def capitalaizer(text):
return text.capitalize()


print(dir(capitalaizer))
# Вывод:

['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']


У некоторых из этих приватных атрибутов есть публичные интерфейсы. К примеру. Используемая в этом примере функция dir() аналогична дандер-методу __dir__.

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

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

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

def rec(var):
if var > 0:
print(var)
var -= 1
return rec(var)


rec(5)
# Вывод:

5

4

3

2

1

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

Вот пример с глобальной переменной:


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

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

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

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

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

def example():
return 'Я - пример'

var_1 = example
var_2 = var_1
print('var_1 is example:', var_1 is example)
print('var_1 is var_2:', var_1 is var_2)
print('example is var_2:', example is var_2)
print('example():', example())
print('var_1():', var_1())
print('var_2():', var_2())
# Вывод:

var_1 is example: True

var_1 is var_2: True

example is var_2: True

example(): Я - пример

var_1(): Я - пример

var_2(): Я - пример

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

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

def_lambda = lambda : print('Hello, world!')
def_lambda()
# Вывод:

Hello, world!


Как и обычные функции, анонимные могут работать с аргументами и возвращать значения:

def_lambda = lambda x, y=6: x * y
print(def_lambda(2))
# Вывод:

12


Их также можно вызывать, не присваивая переменной:

print((lambda x, y=6: x * y)(2))
# Вывод:

12

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

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

def example(var=[]):
var.append('элемент')
print(var)

example()
example()
example()
# Вывод:

['элемент']

['элемент', 'элемент']

['элемент', 'элемент', 'элемент']


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

def example(var=...):
if var is ...:
var = []
var.append('элемент')
print(var)

example()
example()
example()
# Вывод:

['элемент']

['элемент']

['элемент']


Это стоит знать хотя бы потому, что это является очень распространённым вопросом на собеседованиях.

Чистые функции

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

Пример чистой функции:

def example(var):
return var * 2

var_1 = example(4)
print(var_1)
# Вывод:

8


Пример функции с побочным эффектом:

def example(var):
print('Я - побочный эффект')
return var * 2

var_1 = example(4)
print(var_1)
# Вывод:

Я - побочный эффект

8


Здесь побочным эффектом является вывод в консоль.

Оцените статью
О Python на русском языке
Добавить комментарий

Adblock
detector