*args и **kwargs
Прежде чем завершить, поговорим про еще одну важную тему, а именно про так называемые *args (сокращение от arguments) и **kwargs (keyword arguments).
Они позволяют передавать функции различное количество позиционных (*args) или именованных (**kwargs) аргументов.
Рассмотрим на примере. Начнем с *args.
*args
Предположим, что у нас есть простая функция, которая принимает два числа и считает среднее арифметическое.
1 |
# объявим функцию defmean(a,b) return(a+b)2 mean(1,2) |
1 | 1.5 |
Все отлично работает, но мы не можем передать этой функции больше двух чисел. Возможным решением станет функция, которая изначально принимает список в качестве аргумента.
1 |
# объявим функцию, которой нужно передать список defmean(list_) # зададим переменную для суммы, total= # в цикле сложим все числа из списка foriinlist_ total+=i # и разделим на количество элементов returntotallen(list_) |
1 |
# создадим список list_=1,2,3,4 mean(list_) |
1 | 2.5 |
Все опять же работает, но нам каждый раз нужно создавать список. При попытке передать отдельные числа функция выдаст ошибку.
1 | mean(1,2) |
*args позволяет передавать функции произвольное количество отдельных чисел.
1 |
# объявим функцию с *args defmean(*nums) total= foriinnums total+=i returntotallen(nums) |
1 | mean(1,2,3,4) |
1 | 2.5 |
Как вы видите, главным элементом здесь является оператор распаковки * (unpacking operator). Он принимает все передаваемые в функцию числа и формирует из них кортеж.
Затем мы проходимся по элементам этого кортежа, рассчитываем их сумму и делим на количество элементов. Использовать слово args не обязательно, мы назвали наш позиционный аргумент nums.
Если мы по какой-то причине захотим передать функции список, мы можем это сделать.
1 |
# передадим в функцию список mean(*list_) |
1 | 2.5 |
В этом случае мы передаем название списка со звездочкой *.
Для того чтобы продемонстрировать преобразование чисел в кортеж, напишем вот такую несложную функцию.
1 |
deftest_type(*nums) print(nums,type(nums)) |
1 | test_type(1,2,3,4) |
1 | (1, 2, 3, 4) <class ‘tuple’> |
**kwargs
При использовании **kwargs происходит почти то же самое за тем исключением, что мы распаковываем именованные, а не позиционные аргументы. И распаковываем их в словарь, а не в список. Сразу посмотрим на примере.
1 |
deff(**kwargs) returnkwargs.items() |
1 |
# оператор ** примет произвольное количество именованных аргументов f(a=1,b=2) |
1 | dict_items() |
Приведем более сложный пример. Напишем функцию, которая на вход примет произвольное количество чисел (позиционный аргумент), преобразует в кортеж (*args) и рассчитает (mean) и (standard deviation).
Для каждой из метрик мы дополнительно создадим именованный параметр, который определит выводить эту метрику или нет. Параметры мы передадим через **kwargs. Внутри функции из них будет сформирован словарь.
1 |
# nums функция преобразует в кортеж, params — в словарь defsimple_stats(*nums,**params) # если ключ ‘mean’ есть в словаре params и его значение == True if’mean’inparams andparams’mean’==True # рассчитаем среднее арифметическое кортежа nums и округлим # \t — это символ табуляции print(f’mean: \t{np.round(np.mean(nums), 3)}’) # если ключ ‘std’ есть в словаре params и его значение == True if’std’inparams andparams’std’==True # рассчитаем СКО кортежа nums и округлим print(f’std: \t{np.round(np.std(nums), 3)}’) |
Вызовем функцию simple_stats() и передадим ей числа и именованные аргументы.
1 | simple_stats(5,10,15,20,mean=True,std=True) |
1 |
mean: 12.5 std: 5.59 |
Если для одного из параметров задать значение False, функция не выведет соответствующую метрику.
1 | simple_stats(5,10,15,20,mean=True,std=False) |
1 | mean: 12.5 |
Для того чтобы передать параметры списком и словарем, нам нужно использовать операторы распаковки
* и
** соответственно.
1 |
list_=5,10,15,20 settings={‘mean’True,’std’True} simple_stats(*list_,**settings) |
1 |
mean: 12.5 std: 5.59 |
Количество именованных аргументов в **kwargs может быть любым. Ничто не мешает нам добавить еще один параметр.
1 |
# добавим параметр median simple_stats(5,10,15,20,mean=True,std=True,median=True) |
1 |
mean: 12.5 std: 5.59 |
Впрочем, для того чтобы это имело смысл, такой параметр должен быть прописан внутри функции.
В заключение скажу, что все приведенные выше примеры являются учебными и без *args и **kwargs здесь конечно можно обойтись. На практике, они применяются в более сложных конструкциях, например, в так называемых декораторах, однако эта тема выходит за рамки сегодняшнего занятия.
Встроенные функции
В Питоне есть множество встроенных в (1) стандартный функционал (built-in functions) и (2) дополнительные библиотеки (library functions) функций, и мы много раз их использовали.
Рассмотрим функцию для создания гистограммы plt.hist(). Вначале импортируем библиотеки.
1 |
importmatplotlib.pyplot asplt importnumpy asnp |
Сгенерируем данные, которые передадим этой функции (эти же данные мы создавали и использовали на восьмом занятии вводного курса).
1 |
# установим точку отсчета для воспроизведения такого же результата np.random.seed(42) height=list(np.round(np.random.normal(180,10,1000))) |
Теперь построим гистограмму передав ей в качестве параметров и аргументов наши данные и количество интервалов.
1 |
plt.hist(height,bins=10) plt.show() |
Как мы видим, достаточно обратиться к соответствующей библиотеке (
plt), вызвать эту функцию по имени (
hist) и задать параметры и их аргументы (
heightи
bins=10), и будет исполнен тот код, который заложили в нее создатели библиотеки Matplotlib.
Теперь несколько слов про параметры и аргументы функции.
Параметры и аргументы функции
Для начала определимся с терминами:
-
параметр — это то, что запрашивает функция при вызове (например,
bins, количество интервалов) -
аргумент — значение этого параметра (в нашем случае,
10).
Возникает вопрос, что же такое
height? Логично предположить, что это аргумент (ведь это наши данные). Но тогда как функция узнает, какому параметру он соответствует?
Все дело в том, что параметры и их аргументы могут быть позиционными (positional) и именованными (keyword).
В первом случае, достаточно указать аргумент и поставить его в правильном порядке (позиции). Функция сама поймет, какой параметр ей передают. Во втором случае, нужно указать и название параметра, и аргумент.
Позиционный параметр можно сделать именованным, и тогда порядок важен не будет.
1 |
# данные в этой функции обозначаются через x plt.hist(bins=10,x=height) plt.show() |
Кроме того, функция может иметь параметры с аргументами по умолчанию. Это делает такой параметр не обязательным, а значит упрощает и ускоряет вызов функции.
1 |
# у параметра bins есть аргумент по умолчанию (как раз 10 интервалов) plt.hist(height) plt.show() |
Как вы видите, результат во всех трех случаях совершенно одинаковый.
Если вы сомневаетесь в том, какие параметры принимает функция и что является результатом ее работы, полезно обратиться к документации в Интернете. Например, по функции plt.hist() ее можно найти вот здесь⧉.
Стоит отметить, что функция может как принимать один, два или несколько параметров, так и не принимать их вовсе. Например, функция print(), если не указывать параметры, выдает пустую строку.
1 |
print(‘Первая строка’) print() print(‘Третья строка’) |
1 |
Первая строка Третья строка |
Функции и методы
Некоторые функции называются методами. Методы — это функции, которые можно применить только к конкретному объекту. Другими словами, если обычная функция будет выполнена «сама по себе», это просто участок кода, которому дали имя, то методу для исполнения нужен объект (например, строка, список или словарь)
При этом, что важно, у каждого объекта свои методы
Предположим, у нас есть строка, и мы хотим сделать первую букву каждого слова заглавной. Для этого у строки есть метод .title().
1 |
# создаем строковый объект и some_string=’machine learning’ some_string.title() |
1 | ‘Machine Learning’ |
А теперь попробуем применить этот метод к списку, состоящему из тех же слов, что и строка.
1 |
some_list=’machine’,’learning’ some_list.title() |
Как мы видим, Питон выдал ошибку.
Деструктуризация объекта
Базовый синтаксис деструктуризации объекта довольно прост:
const { identifier } = expression;
Здесь это имя свойства, к которому необходимо получить доступ, а выражение, которое должно возвращать объект или им являться. После деструктуризации переменная содержит значение одноименного свойства объекта.
Рассмотрим следующий пример:
const hero = { name: 'Batman' }; // Деструктуризация объекта const { name } = hero; console.log(name); // => 'Batman'
Инструкция выполняет деструктуризацию объекта. Деструктуризация определяет переменную , которой передается значение свойства объекта .
Когда вы привыкнете к синтаксису деструктуризации объектов, то обнаружите, что это отличный способ в одной инструкции кода извлечь значения свойств некоторого объекта во вновь создаваемые переменные.
Обратите внимание, что вы можете извлечь значения стольких свойств, сколько захотите:
const { identifier1, identifier2, .., identifierN } = expression;
3.1 Используем псевдоним переменной для деструктуризации
Если вы хотите получить доступ к свойству некоторого объекта, но при этом создать переменную с именем отличным от названия его свойства, то вы можете использовать ее псевдоним.
const { identifier: aliasIdentifier } = expression;
Здесь имя свойства к которому мы хотим получить доступ, имя, создаваемой переменной, а соответственно содержит или возвращает объект. После деструктуризации переменная будет содержать значение свойства .
Рассмотрим следующий пример:
const hero = { name: 'Batman' }; // Деструктуризация объекта: const { name: heroName } = hero; console.log(heroName); // => 'Batman'
В инструкции происходит деструктуризация объекта . В ходе его деструктуризации определяется новая переменная (вместо имени соответствующего названию свойства, как в предыдущем примере) и присваивает значение .
3.2 Динамическое имя свойства
Что именно делает деструктуризацию объектов еще более полезной, так это то, что вы можете извлекать в переменные их свойства с динамически изменяющимися именами:
const { : identifier } = expression2;
Первое выражение возвращает имя свойства к которому мы хотим получить доступ. В указывается имя переменной, создающейся после деструктуризации, в которую затем будет передано извлекаемое значение. Второе выражение должно возвращать или содержать объект, который мы хотели бы деструктурировать.
Рассмотрим следующий пример:
const property = 'name'; const hero = { name: 'Batman' }; // Деструтуризация объекта: const { : name } = hero; console.log(name); // => 'Batman'
Так в инструкции мы деструктурируем объект и далее динамически, во время выполнения кода, определяем название его свойства, которое нам необходимо извлечь.
Подведем итог
Сегодня мы впервые поговорили про функции в программировании и выяснили, чем они отличаются от функций в математике. Кроме того мы узнали, что в Питоне можно:
- использовать готовые функции, которые уже встроены либо в базовый функционал, либо в дополнительную библиотеку;
- объявлять собственные функции через ключевое слово def и название функции; а также
- создавать анонимные или lambda-функции, которые очень удобно применять там, где в полноценных собственных функциях нет необходимости
Помимо этого, мы выяснили, что любой функции можно передать параметры и аргументы, которые в зависимости от способа передачи могут быть позиционными или именованными.
Мы узнали, что у переменной может быть локальная и глобальная области видимости.
Наконец, мы поговорили про возможность передачи различного количества позиционных и именованных аргументов через *args и **kwargs.
Вопросы для закрепления
Какие три вида функций мы изучили?
Посмотреть правильный ответ
Ответ: встроенные, собственные, а также анонимные или lambda-функции.
Какие бывают параметры и аргументы функции?
Посмотреть правильный ответ
Ответ: позиционные (в этом случае мы указываем только аргумент, но ставим его в определенном порядке) и именованные (указываем и параметр, и аргумент, но порядок не важен).
Какова область видимости локальной переменной?
Посмотреть правильный ответ
Ответ: область видимости локальной переменной ограничена той функцией, в которой эта переменная была объявлена.
В ноутбуке к лекции приведены ⧉.
В следующий раз мы подробнее рассмотрим списки, кортежи и множества.