Создание функции в Python

Функции – это многократно используемые фрагменты программы. Они позволяют дать имя определённому блоку команд с тем, чтобы впоследствии запускать этот блок по указанному имени в любом месте программы и сколь угодно много раз. Это называется вызовом функции. Мы уже использовали много встроенных функций, как то len и range.

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

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

def sayHello():
    print('Привет, Мир!') # блок, принадлежащий функции
    # Конец функции

sayHello() # вызов функции
sayHello() # ещё один вызов функции


Термины и определения

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

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

Затем идет двоеточие :, которое завершает строку определения функции.

Дальше идет новая строка, начинающаяся с отступа (4 пробела или одно нажатие клавиши tab).

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

# объявим функцию my_function()
def my_function():
    # тело функции

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

Параметры указываются в скобках при объявлении функции и разделяются запятыми.

Аналогично мы передаём значения, когда вызываем функцию.

Ещё раз: Обратите внимание на терминологию: имена, указанные в объявлении функции, называются параметрами(то что в скобках), тогда как значения, которые вы передаёте в функцию при её вызове, — аргументами.

# a, b - параметры функции
def test(a, b): # создание функции
    # внутри зделай что-нибудь

# 120, 404 — аргументы
test(120, 404) # вызов функции

Сперва необходимо создать функцию – потом вызвать

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

  • Без аргументов (в скобках ничего не записанно)
  • Обязательные аргументы (Required arguments)
  • Аргументы-ключевые слова (Keyword argument)
  • Аргументы по умолчанию (Default argument)
  • Аргументы произвольной длины (Variable-length argumens)

Без аргументов

(В скобках нет аргументов)

#создаём функцию
def hello_world():  
    print("hello world")  

# вызываем функцию
hello_world()  

Обязательные аргументы функции:

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

Например:

def bigger(a,b):
    if a > b:
        print a
    else:
       print b
 
# В описании функции указано, что она принимает 2 аргумента а и b
 
# Вызов и корректное использование функции
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="Звезды"):
    print (planet_name, "находится на орбите", center)
 
# Можно вызвать функцию space так:
space("Mars")
# В результате получим: Mars находится на орбите Звезды
 
# Можно вызвать функцию space иначе:
space("Mars", "черной дыры")
# В результате получим: Mars находится на орбите черной дыры

Аргументы произвольной длины

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

В этом случае следует использовать аргументы произвольной длины. Они задаются произвольным именем переменной, перед которой ставится звездочка (*).

Например:

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)

print(num)

Область видимости

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

В Python две базовых области видимости переменных:

  • Глобальные переменные
  • Локальные переменные

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

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

Например:

# глобальная переменная age
age = 44
 
def info():
    print age # Печатаем глобальную переменную age
 
def local_info():
    age = 22 # создаем локальную переменную age 
    print age
 
info() # напечатает 44
local_info() # напечатает 22

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

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

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

Однако, это не приветствуется, и его следует избегать, поскольку человеку, читающему текст программы, будет непонятно, где находится объявление переменной. Использование зарезервированного слова global достаточно ясно показывает, что переменная объявлена в самом внешнем блоке.

Например:

x = 50

def func():
    global x

    print('x равно', x)
    x = 2
    print('Заменяем глобальное значение x на', x)

func()
print('Значение x составляет', x)

Вывод:

$ python func_global.py
x равно 50
Заменяем глобальное значение x на 2
Значение x составляет 2

Или ещё пример:

# глобальная переменная age
age = 13
 
# функция изменяющая глобальную переменную
def get_older():
    global age
    age += 1
 
print age # напечатает 13
get_older() # увеличиваем age на 1
print age # напечатает 14

Как это работает:

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

Используя одно зарезервированное слово global, можно объявить сразу несколько переменных: global x, y, z.

Зарезервированное слово “nonlocal”

Мы увидели, как получать доступ к переменным в локальной и глобальной области видимости. Есть ещё один тип области видимости, называемый “нелокальной” (nonlocal) областью видимости, который представляет собой нечто среднее между первыми двумя. Нелокальные области видимости встречаются, когда вы определяете функции внутри функций.

Поскольку в Python всё является выполнимым кодом, вы можете определять функции где угодно.

Давайте рассмотрим пример:

# Filename: func_nonlocal.py

def func_outer():
    x = 2
    print('x равно', x)

    def func_inner():
        nonlocal x
        x = 5

    func_inner()
    print('Локальное x сменилось на', x)

func_outer()

Вывод:

$ python func_nonlocal.py
x равно 2
Локальное x сменилось на 5

Как это работает:

Когда мы находимся внутри func_inner, переменная x, определённая в первой строке func_outer находится ни в локальной области видимости (определение переменной не входит в блок func_inner), ни в глобальной области видимости (она также и не в основном блоке программы). Мы объявляем, что хотим использовать именно эту переменную x, следующим образом: nonlocal x.

Попробуйте заменить “nonlocal x” на “global x“, а затем удалить это зарезервированное слово, и пронаблюдайте за разницей между этими двумя случаями.

Функции в Python (повтор темы)

Определение

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

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__

модуля 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()