Функции в python

Каррирование: частичное фиксирование аргументов функции

В информатике термином каррирование обозначается процедура порождения новых функций из существующих путем фиксирования некоторых аргументов.

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

def add_numbers(x, y):
    return x + y

Мы можем породить на ее основе новую функцию одной переменной, add_five, которая прибавляет к своему аргументу 5:

add_five = lambda y: add_numbers(5, y)

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

Теперь запустим полный код:

def add_numbers(x, y):
    return x + y

add_five = lambda y: add_numbers(5, y)

print(add_five(10))

Результат:

15

Стандартный модуль functools упрощает эту процедуру за счет функции partial:

from functools import partial

def add_numbers(x, y):
    return x + y

add_five = partial(add_numbers, 5)

print(add_five(10))

Также вернет

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

# вычислить скользящее среднее временного ряда x за 60 дней
ma60 = lambda x: pandas.rolling_mean(x, 60)
# вычислить скользящие средние за 60 дней всех временных рядов в data
data.apply(ma60)

5
5
голоса

Рейтинг статьи

Используем аргументы при декорировании функций

Наш простейший декоратор, который мы реализовали выше, будет работать только для функций, которые не требуют передачи в них параметров. И вызов нашей функции с параметрами потерпит неудачу и приведет к возбуждению исключения типа TypeError, если мы попытаемся декорировать функцию, принимающую аргументы из функции (декоратора). Давайте создадим другой декоратор и назовем его , он в качестве параметра принимает функцию, которая, в свою очередь, возвращает строку, преобразованную ее в верхний регистр.

def yell(func):
    def wrapper(*args, **kwargs):
        val = func(*args, **kwargs)
        val = val.upper() + "!"
        return val

    return wrapper

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

@yell
def hello(name):
    return f"Hello {name}"
hello("redowan")
>>> 'HELLO REDOWAN!'

Функция принимает строку в качестве параметра и возвращает сообщение в виде трансформированной строки. И так наш декоратор изменяет возвращаемую целевой функций строку, преобразует ее в верхний регистр и добавляет символ без непосредственного изменения кода в функции .

Что такое функция?

Функция – это изолированный блок кода, который решает отдельную задачу.

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

Функции также помогают организовать ваш код.

Если нужно внести какое-то изменение в код функции, обновить придется только ее. Без функций у вас были бы многократно повторяющиеся куски одинакового кода в разных местах, и обновлять этот код пришлось бы повсюду.

Если говорить о принципах программирования, использование функций — это следование принципу DRY (Don’t Repeat Yourself — «не повтоярйся»).

Код внутри функции запускается только тогда, когда функцию вызывают.

Функции могут принимать аргументы и значения по умолчанию. При вызове они могут возвращать какое-то значение в вызвавший их код, но могут и не возвращать ничего.

Вложенные области видимости и lambda-выражения

lambda (как и def) порождает новую область видимости.

То же самое, с ручной передачаей значения (работет и в старых версиях питона):

Когда нужно передать значение в lambda вручную? Если мы используем lambda в цикле.
Хотим, чтобы каждая lambda запомнила свое значение i.
Получилось, что все lambda запомнили последнее значение i (4).

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

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

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

Передача аргумента и изменчивость

Сначала немного терминологии:

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

В Python, аргументы передаются по заданию (в отличие от других языков, где аргументы могут передаваться по значению / задания / указателя).

Мутирование параметра приведет к изменению аргумента (если тип аргумента является изменяемым).

Переназначение параметра не переназначит аргумент.

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

  • Неизменные: Целые, строки, кортежи, и так далее. Все операции делают копии.
  • Mutable: списки, словари, наборы, и так далее. Операции могут или не могут мутировать.

Замыкания

Примеры внутренних функций, определяемых в других функциях, мы разобрали в предыдущем разделе. Такие вложенные функции могут получить доступ к переменным из области видимости внешней (оборачивающей ее) функции. В Python по умолчанию предусмотрена возможность использования в функциях таких нелокальных переменных, и если мы хотим изменять их значения в коде внутренней, то должны объявить их нелокальными non-local явно (с ключевым словом ). Ниже приведён пример вложенной функции, получающей нелокальную переменную из внешней (по умолчанию без использования ключевого слова ).

def burger(name):
    
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients

Теперь запустим следующий код на выполнение:

ingr = burger("deli")
dish = ingr()
print(dish)
>>> ('steak', 'pastrami', 'emmental')

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

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

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

Значение переменной из области видимости внешней функции запоминается даже в том случае, если сама переменная выходит из текущей области видимости, а также если сама (внешняя) функция удаляется из текущего контекста исполнения (пространства имен). Декораторы в своей работе используют тот же принцип применения нелокальных non-local переменных и скоро вы убедитесь в этом сами.

5.1.1. Основные понятия и механизм работы¶

Подпрограмма должна быть объявлена и в общем случае содержать:

  • имя;

  • список имен и типов передаваемых параметров (необязательно);

  • тип возвращаемого значения (необязательно).

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

Большинство современных языков программирования для управления вызовом подпрограмм используют стек вызовов.

Примерный цикл работы стека вызова следующий (Видео 5.1.1, Видео 5.1.2):

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

  2. Когда подпрограмма завершается, запись удаляется из стека и программа продолжает выполняться, начиная с адреса возврата.

Видео 5.1.1 — Cтек вызовов (пример, Нетология)

Your browser does not support the video tag.

Видео 5.1.2 — Cтек вызовов (пример, Stepik.org)

4.4. Операторы break и continue, а также уточнение циклов else¶

Как и в случае с C, оператор выходит из самой внутренней обёртки
цикла или .

Операторы цикла могут содержать уточнение ; выполняется при
выполнении цикла заканчивается исчерпанием итератива (с ) или
когда состояние становится ложным (с ), но это не тот случае, если
цикл прерван оператором . Это поведение иллюстрируется следующим
примером, в котором производится поиск простых чисел:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 
...             print(n, ' равно ', x, '*', n//x)
...             break
...     else
...         # цикл закончился, не найдя множителя
...         print(n, ' является простым числом')
...
2 является простым числом
3 является простым числом
4 равно 2 * 2
5 является простым числом
6 равно 2 * 3
7 является простым числом
8 равно 2 * 4
9 равно 3 * 3

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

При использовании с циклом уточнение имеет больше общего с
уточнения оператора , чем уточнение
оператора. Уточнение оператора выполняется при отсутствии
исключений и выполнение уточнения цикла при отсутствии .
Дополнительные сведения о операторе и исключениях см. в
разделе .

Оператор также заимствованна у C, переходя к следующей
итерации цикла:

Несколько аргументов[править]

Предыдущие функции F(C) и F2(C) функции одной переменной, С, то есть функции принимают только один аргумент. Но вообще функции могут принимать сколь угодно много аргументов. Например, задачу из нашего первого урока в виде функции двух аргументов можно записать так:

def yfunc(t, v0):
    g = 9.81
    return v0*t - 0.5*g*t**2

Отметим, что g локальная переменная с фиксированным значением, в то время как t и v0 аргументы функции, также являющиеся ее локальными переменными. Как можно вызвать заданную функцию нескольких аргументов:

y = yfunc(0.1, 6)
y = yfunc(0.1, v0=6)
y = yfunc(t=0.1, v0=6)
y = yfunc(v0=6, t=0.1)

Возможность при вызове передавать аргументы в виде аргумент=значение делает чтение кода более простым для понимания. При этом, если использовать данную конструкцию для всех аргументов, можно не думать об их очередности. Заметим, что если данный синтаксис выполняется не для всех аргументов, то требуется соблюдать последовательность: вначале значения «безымянных» аргументов, потом в виде аргумент=значение. То есть вызов yfunc(t=0.1, 6) будет некорректным.

Независимо от того пишем ли мы yfunc(0.1, 6) или yfunc(v0=6, t=0.1), аргументы рассматриваются как локальные переменные подобно присваиванию переменным значений:

t = 0.1
v0 = 6

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

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

def yfunc(t):
    g = 9.81
    return v0*t - 0.5*g*t**2

Главное отличие в том, что теперь v0 просто обязана быть глобальной переменной, иначе мы не можем вызвать yfunc:

>>> def yfunc(t):
...     g = 9.81
...     return v0*t - 0.5*g*t**2
...
>>> yfunc(0.6)
...
NameError global name 'v0' is not defined

Решением служит предопределение v0 как глобальной переменной перед вызовом yfunc:

>>> v0 = 5
>>> yfunc(0.6)
1.2342

Наши функции Python пока что недалеко уходили от обычных математических функций. Но у функций в программировании существует гораздо больше применений. Любой набор инструкций, который мы хотим неоднократно выполнять над однотипными объектами тут же попадает в кандидаты для функции Python. Скажем, нам нужно составлять списки чисел начинающиеся с одного заданного значения и заканчивающиеся другим заданным значением с определенным шагом. Наши функции, например, нуждаются в таких списках для значений С или t. Давайте напишем функцию, выполняющую такое задание:

def makelist(start, stop, inc):
    value = start
    result = []
    while value <= stop
        result.append(round(value, 1))  
        value = value + inc
    return result

mylist =  makelist(, 100, 0.2)
print mylist  #  выведет 0, 0.2, 0.4, 0.6, ... 99.8, 100

Функции в качестве аргументов[править]

Программы, что-то рассчитывающие часто требуют, чтобы функции были аргументами для других функций. Например, нам требуется вычислить численно вторую производную от функции f(x):

f″(x)≈f(x−h)−2f(x)+f(x+h)h2{\displaystyle f»(x)\approx {\frac {f(x-h)-2f(x)+f(x+h)}{h^{2}}}}

где h — малое число, уменьшая которое мы находим достаточно точный ответ. Функция Python для такой задачи может быть записана следующим образом:

def diff2(f, x, h=1E-6):
    r = (f(x-h) - 2*f(x) + f(x+h))float(h*h)
    return r

Аргумент f здесь выступает как любой другой аргумент. Применение diff2 выглядит, например, так:

def g(t):
    return t**(-6)

t = 1.2
d2g = diff2(g, t)
print "g''(%f)=%f" % (t, d2g)

Функции одной переменной[править]

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

def F(C):
    return (9.05)*C + 32

Задание функции в Python начинается со слова def, за ним следует имя функции и затем после двоеточия на новой строке с отступом начинаются инструкции функции. Слово return означает, что функция возвращает значение, которое находится после этого слова. Это значение связывается с именем функции. То есть передав в функцию аргумент (С), на выходе мы имеем значение, рассчитанное после слова return.

Строка с def, именем и аргументами функции называется заголовком функции, в то время как оставшаяся часть из самих инструкций — телом функции.

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

a = 10
F1 = F(a)
temp = F(15.5)
print F(a+1)
sum_temp = F(10) + F(20)

В нашем примере при вызове возвращается объект типа float, который может быть использован далее в любом месте кода, где может использоваться объект типа float. Например, в инструкции print. А вот так из списка градусов Цельсия можно создать соответствующий список по Фаренгейту:

Fdegrees = F(C) for C in Cdegrees

Немного поработав, получим и форматированный вывод, в котором уже возвращается объект строкового типа:

>>> def  F2(C):
...     F_value = (9.05)*C + 32
...     return '%.1f degrees Celsius  corresponds to '\
...            '%.1f degrees Fahrenheit' % (C, F_value)
...
>>> s1 = F2(21)
>>> s1
'21.0 degrees Celsius corresponds to 69.8 degrees Fahrenheit'

Return

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

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

Python 3 предлагает заявление ( https://codecamp.ru/documentation/python/263/variable-scope-and-binding/5712/nonlocal-variables#t=201608272008282346874 ) для реализации полного закрытия с вложенными функциями.

def makeInc (x): def inc (y): нелокальный x # теперь можно присвоить значение x x + = y вернуть x return inc incOne = makeInc (1) incOne (5) # возвращает 6

Модуль getopt

Как вы могли заметить ранее, модуль sys разбивает строку командной строки только на отдельные фасеты. Модуль getopt в Python идет немного дальше и расширяет разделение входной строки проверкой параметров. Основанный на функции C getopt, он позволяет использовать как короткие, так и длинные варианты, включая присвоение значений.

На практике для правильной обработки входных данных требуется модуль sys. Для этого необходимо заранее загрузить как модуль sys, так и модуль getopt. Затем из списка входных параметров мы удаляем первый элемент списка (см. код ниже) и сохраняем оставшийся список аргументов командной строки в переменной с именем arguments_list.

# Include standard modules
import getopt, sys

# Get full command-line arguments
full_cmd_arguments = sys.argv

# Keep all but the first
argument_list = full_cmd_arguments

print argument_list

Аргументы в списке аргументов теперь можно анализировать с помощью метода getopts(). Но перед этим нам нужно сообщить getopts() о том, какие параметры допустимы. Они определены так:

short_options = "ho:v"
long_options = 

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

------------------------------------------
long argument   short argument  with value
------------------------------------------
--help           -h              no
--output         -o              yes
--verbose        -v              no
------------------------------------------

Вы могли заметить, что после опции o short ставится двоеточие (:). Это сообщает getopt, что этой опции следует присвоить значение.

Теперь это позволяет нам обрабатывать список аргументов. Для метода getopt() необходимо настроить три параметра – список фактических аргументов из argv, а также допустимые короткие и длинные параметры (показаны в предыдущем фрагменте кода).

Сам вызов метода хранится в инструкции try-catch, чтобы скрыть ошибки во время оценки. Исключение возникает, если обнаруживается аргумент, который не является частью списка, как определено ранее. Скрипт в Python выведет сообщение об ошибке на экран и выйдет с кодом ошибки 2.

try:
    arguments, values = getopt.getopt(argument_list, short_options, long_options)
except getopt.error as err:
    # Output error, and return with an error code
    print (str(err))
    sys.exit(2)

Наконец, аргументы с соответствующими значениями сохраняются в двух переменных с именами arguments и values. Теперь вы можете легко оценить эти переменные в своем коде. Мы можем использовать цикл for для перебора списка распознанных аргументов, одна запись за другой.

# Evaluate given options
for current_argument, current_value in arguments:
    if current_argument in ("-v", "--verbose"):
        print ("Enabling verbose mode")
    elif current_argument in ("-h", "--help"):
        print ("Displaying help")
    elif current_argument in ("-o", "--output"):
        print (("Enabling special output mode (%s)") % (current_value))

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

$ python arguments-getopt.py -h
Displaying help
$ python arguments-getopt.py --help
Displaying help
$ python arguments-getopt.py --output=green --help -v
Enabling special output mode (green)
Displaying help
Enabling verbose mode
$ python arguments-getopt.py -verbose
option -e not recognized

Последний вызов нашей программы поначалу может показаться немного запутанным. Чтобы понять это, вам нужно знать, что сокращенные параметры (иногда также называемые флагами) могут использоваться вместе с одним тире. Это позволяет вашему инструменту легче воспринимать множество вариантов. Например, вызов аргументов arguments-getopt.py-vh аналогичен вызову аргументов arguments-getopt.py-v-h. Таким образом, в последнем вызове выше модуль getopt подумал, что пользователь пытается передать -e в качестве опции, что недопустимо.

Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Люкс-хост
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: