Шорты Python
Праймеры по * args, ** kwargs, декораторам для Data Scientists
Понимание жизненно важно, когда дело доходит до кодирования
Python имеет множество конструкций, которые достаточно легко изучить и использовать в нашем коде. Кроме того, есть некоторые конструкции, которые всегда сбивают нас с толку, когда мы встречаем их в нашем коде.
Есть такие, которые не могут понять даже опытные программисты. *args, **kwargs
и декораторы - некоторые конструкции, которые попадают в эту категорию.
Думаю, многие мои друзья из области науки о данных тоже сталкивались с ними.
Большинство функций seaborn так или иначе используют * args и ** kwargs.
А как насчет декораторов?
Каждый раз, когда вы видите предупреждение о том, что какая-то функция будет устаревшей в следующей версии. В пакете sklearn
для этого используются декораторы. Вы можете увидеть @deprecated
в исходном коде. Это функция декоратора.
В этой серии публикаций под названием Python Shorts я объясню некоторые простые конструкции, предоставляемые Python, некоторые важные советы и некоторые варианты использования, которые я регулярно придумываю в своей работе с Data Science.
Этот пост посвящен простому для понимания объяснению некоторых сложных концепций.
Что такое * args?
Проще говоря, вы можете использовать *args
, чтобы указать произвольное количество входов для вашей функции.
Простой пример:
Допустим, нам нужно создать функцию, которая складывает два числа. Мы легко можем сделать это на Python.
def adder(x,y): return x+y
Что, если мы хотим создать функцию для добавления трех переменных?
def adder(x,y,z): return x+y+z
Что, если мы хотим, чтобы та же функция добавляла неизвестное количество переменных? Обратите внимание, что для этого мы можем использовать *args
, *argv
или *anyOtherName
. Это *
, что имеет значение.
def adder(*args): result = 0 for arg in args: result+=arg return result
Что делает *args
, так это то, что он принимает все ваши переданные аргументы и предоставляет список аргументов переменной длины для функции, которую вы можете использовать по своему усмотрению.
Теперь вы можете использовать ту же функцию следующим образом:
adder(1,2) adder(1,2,3) adder(1,2,5,7,8,9,100)
и так далее.
А вы когда-нибудь задумывались, как функция печати в Python может принимать столько аргументов? *args
Что такое ** kwargs?
Проще говоря, вы можете использовать **kwargs
, чтобы задать произвольное количество вводов с ключевыми словами для вашей функции и получить к ним доступ с помощью словаря.
Простой пример:
Допустим, вы хотите создать функцию печати, которая может принимать имя и возраст в качестве входных данных и распечатывать их.
def myprint(name,age): print(f'{name} is {age} years old')
Простой. Допустим, вы хотите, чтобы одна и та же функция принимала два имени и два возраста.
def myprint(name1,age1,name2,age2): print(f'{name1} is {age1} years old') print(f'{name2} is {age2} years old')
Вы правильно угадали, мой следующий вопрос: Что делать, если я не знаю, сколько аргументов мне понадобится?
Могу я использовать *args
? Думаю, нет, потому что имя и возраст важны. Мы не хотим писать «Михаилу 28 лет».
Приходите **kwargs
на картинке.
def myprint(**kwargs): for k,v in kwargs.items(): print(f'{k} is {v} years old')
Вы можете вызвать эту функцию, используя:
myprint(Sansa=20,Tyrion=40,Arya=17) Output: ----------------------------------- Sansa is 20 years old Tyrion is 40 years old Arya is 17 years old
Помните, что мы никогда не определяли Сансу, Арью или Тирион в качестве аргументов наших методов.
Это довольно мощная концепция. И многие программисты довольно умно используют ее при написании библиотек-оболочек.
Например, функция seaborn.scatterplot
является оболочкой для функции plt.scatter
из Matplotlib. По сути, используя *args
и **kwargs
, мы можем предоставить все аргументы, которые plt.scatter
может принимать и seaborn.Scatterplot
.
Это может сэкономить много усилий при написании кода, а также делает код надежным в будущем. Если когда-нибудь в будущемplt.scatter
начнет принимать какие-либо новые аргументы, seaborn.Scatterplot
функция все равно будет работать.
Что такое декораторы?
Проще говоря: Декораторы - это функции, которые обертывают другую функцию, изменяя ее поведение.
Простой пример:
Допустим, мы хотим добавить пользовательские функции к некоторым из наших функций. Функциональность заключается в том, что всякий раз, когда функция вызывается, печатается «function name
начинается», а всякий раз, когда функция завершается, печатается «function name
заканчивается» и время, затраченное функцией.
Предположим, наша функция:
def somefunc(a,b): output = a+b return output
Для этого мы можем добавить несколько строк печати ко всем нашим функциям.
import time def somefunc(a,b): print("somefunc begins") start_time = time.time() output = a+b print("somefunc ends in ",time.time()-start_time, "secs") return output out = somefunc(4,5) OUTPUT: ------------------------------------------- somefunc begins somefunc ends in 9.5367431640625e-07 secs
Но можем ли мы сделать лучше?
Здесь декораторы преуспевают. Мы можем использовать декораторы, чтобы обернуть любую функцию.
from functools import wraps def timer(func): @wraps(func) def wrapper(a,b): print(f"{func.__name__!r} begins") start_time = time.time() result = func(a,b) print(f"{func.__name__!r} ends in {time.time()-start_time} secs") return result return wrapper
Так мы можем определить любой декоратор. functools
помогает нам создавать декораторы с помощью wraps
. По сути, мы делаем что-то перед вызовом какой-либо функции и делаем что-то после того, как функция вызывается в указанном выше декораторе.
Теперь мы можем использовать этот timer
декоратор, чтобы украсить нашу функцию somefunc
@timer def somefunc(a,b): output = a+b return output
Теперь, вызывая эту функцию, мы получаем:
a = somefunc(4,5) Output --------------------------------------------- 'somefunc' begins 'somefunc' ends in 2.86102294921875e-06 secs
Теперь мы можем добавить @timer
к каждой функции, для которой мы хотим напечатать время. И мы закончили.
Действительно?
Соединяем все части
Что, если наша функция принимает три аргумента? Или много аргументов?
Вот где соединяется то, что мы узнали до сих пор. Мы используем *args
и **kwargs
Мы меняем нашу функцию декоратора как:
from functools import wraps def timer(func): @wraps(func) def wrapper(*args,**kwargs): print(f"{func.__name__!r} begins") start_time = time.time() result = func(*args,**kwargs) print(f"{func.__name__!r} ends in {time.time()-start_time} secs") return result return wrapper
Теперь наша функция может принимать любое количество аргументов, и наш декоратор по-прежнему будет работать.
Разве Python не прекрасен?
На мой взгляд, декораторы могут быть очень полезны. Я предоставил только один вариант использования декораторов, но есть несколько способов их использования.
Вы можете использовать декоратор для отладки кода, проверяя, какие аргументы входят в функцию. Или декоратор можно использовать для подсчета количества вызовов конкретной функции. Это может помочь в подсчете рекурсивных вызовов.
Заключение
В этом посте я рассказал о некоторых конструкциях, которые можно найти в исходном коде python, и о том, как их понять.
Необязательно, чтобы вы в конечном итоге использовали их в своем коде сейчас. Но я полагаю, что понимание того, как эти вещи работают, помогает смягчить некоторую путаницу и панику, с которыми человек сталкивается всякий раз, когда возникают эти конструкции.
Понимание жизненно важно, когда дело доходит до кодирования
Также, если вы хотите узнать больше о Python 3, я хотел бы назвать отличный курс Learn Intermediate level Python от Мичиганского университета. Проверьте это.
В будущем я также буду писать больше постов для начинающих. Сообщите мне, что вы думаете о сериале. Подпишитесь на меня в Medium или подпишитесь на мой блог, чтобы быть в курсе о них. Как всегда, я приветствую отзывы и конструктивную критику, и с ними можно связаться в Twitter @mlwhiz.