Рубрики
Без рубрики

Погружение В Закрытие Python и Декораторы – Часть 1

Мы будем рассматривать локальные функции, концепции закрытия и то, что такое декораторы, звучит как много? Тогда давайте погрузимся в это.

Автор оригинала: Moyosore Sosan.

Мы будем рассматривать локальные функции, концепции закрытия и то, что такое декораторы, звучит как много? Тогда давайте погрузимся в это.

Локальные функции

Вероятно, большинство функций, с которыми вы знакомы, определены либо в модуле/глобальной области, либо в классах, т. Е. методах. Однако Python позволяет нам определять наши функции в локальной области, то есть внутри функции. Зная это, локальные функции-это функции, определенные в другой функции. Мы говорим, что эти функции определены в рамках конкретной функции, например

def my_function():
  def my_local_function():
    print('I am a local function, local to my_function')
  print('I am not a local function')

Функция my_local_function является локальной функцией, поскольку она доступна только внутри my_function и может использоваться только внутри функции. Давайте рассмотрим другой пример:

def remove_first_item(my_array):
  def get_first_item(s):
    return s[0]
  my_array.remove(get_first_item(my_array))
  return my_array
    
print(remove_first_item(['1','2','3'])))	# ['2','3']
print(get_first_item([1,2,3,4]))
# NameError: name 'get_first_item' is not defined

Из вышесказанного мы видим, что вызов get_first_item выдает ошибку. Это связано с тем, что он может быть доступен только в функции remove_first_item , что делает его локальной функцией.

Локальные функции также могут быть возвращены из функций. Возврат локальной функции аналогичен возврату в любом другом объекте. Давайте посмотрим:

def enclosing_func():
  def local_func():
    print ('I am a local function')
  return local_func()
 
 print(enclosing_func())	# I am a local function'

Локальные функции подчиняются тем же правилам области видимости, что и другие функции, это приводит к правилу LEGB для поиска имен в python – проверка начинается с локальной области, затем с заключающей, затем глобальной и, наконец, встроенной области LEGB- Локальной, Эклектичной, глобальной, встроенной

x = 'global'
def outer_func():
  y = 'enclose'
  def inner_func():
    z = 'local'
    print(x, y, z)
  inner_func()
  
 print(outer_func())	# global enclose local

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

Закрытие

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

def outer_func():
  x = 5
  def inner_func(y = 3):
    return (x + y)
  return inner_func
a = outer_func()
 
print(a())	# 8

Из вышесказанного мы видим, что функция inner_func ссылается на функцию outer_func для значения x. Локальная функция может ссылаться на внешнюю область через closures . Замыкания поддерживают ссылки на объекты из более ранней области.

Закрытие обычно используется в том, что называется Function Factory – это функции, которые возвращают другие функции. Возвращаемые функции являются специализированными. Фабрика функций принимает аргументы, создает локальную функцию, которая создает свои собственные аргументы, а также использует аргументы, переданные фабрике функций. Это возможно при закрытии

def multiply_by(num):
  def multiply_by_num(k):
    return num * k
  return multiply_by_num
  
five = multiply_by(5)
print(five(2))	# 10
print(five(4))	# 20
 
decimal = multiply_by(10)
print(decimal(20))	# 200
print(decimal(3))	# 30

Здесь мы видим, что локальная функция multiply_by_num принимает аргумент k и возвращает аргумент, умноженный на аргумент ее заключающей функции. Кроме того, функция multiply_by принимает аргумент num и возвращает функцию, которая умножает num на свой аргумент.

Правило LEGB не применяется при появлении новых привязок имен

text = "global text"
def outer_func():
  text = "enclosing text"
  def inner_func():
    text = "inner text"
    print('inner_func:', text)	# inner_func: global text
  print('outer_func:', text)	# outer_func: enclosing text
  inner_func()
  print('outer_func:', text)	# outer_func: enclosing text
 
print('global:', text)	# global: global text
outer_func()
print('global:', text)	# global: global text

В функции inner_func мы создали новую привязку имени, переназначив переменную text в области действия функции. Вызов inner_func не влияет на переменную text в outer_func , аналогично вызов outer_func не влияет на глобальную переменную text .

глобальный

global – это ключевое слово python, которое вводит имена из глобального пространства имен в локальное пространство имен. Из предыдущего кода мы можем сделать inner_func , чтобы изменить переменную text , а не создавать новую.

text = "global text"
def outer_func():
  text = "enclosing text"
  def inner_func():
  	global text	# binds the global text to the local text
  	text = "inner text"
  	print('inner_func:', text)	# inner_func: inner text
  print('outer_func:', text)	# outer_func: enclosing text
  inner_func()
  print('outer_func:', text)	# outer_func: enclosing text
 
print('global:', text)	# global: global text
outer_func()
print('global:', text)	# global: inner text

Поскольку ключевое слово global связало глобальную text переменную с локальной text переменной, вызов функции outer_func вносит изменения в глобальный текст, следовательно, переназначая переменную text в глобальной области.

нелокальный

Ключевое слово nonlocal позволяет нам вводить имена из окружающего пространства имен в локальное пространство имен. Все еще глядя на предыдущий код:

text = "global text"
def outer_func():
  text = "enclosing text"
  def inner_func():
  	nonlocal text	# binds the local text to the enclosing text
  	text = "inner text"
  	print('inner_func:', text)	# inner_func: inner text
  print('outer_func:', text)	# outer_func: enclosing text
  inner_func()
  print('outer_func:', text)	# outer_func: inner text
 
print('global:', text)	# global: global text
outer_func()
print('global:', text)	# global: global text

Здесь заключающая переменная text изменяется при вызове inner_func .

Декораторы

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

# Syntax for decorator
 
@my_decorator
def my_function():
  pass

Результат вызова my_function передается в my_decorator

def capitalize(func):
  def uppercase():
    result = func()
    return result.upper()
  return uppercase
 
@capitalize
def say_hello():
  return "hello"
  
print(say_hello())	# 'HELLO'

Результат вызова say_hello передается в декоратор с заглавной буквы. Декоратор изменяет функцию say_hello , изменяя ее результат на верхний регистр. Мы видим, что декоратор capitalize принимает вызываемый объект(say_hello) в качестве аргумента и возвращает другой вызываемый объект(верхний регистр). Это всего лишь базовый пример на декораторе

В следующем посте мы продолжим разбираться в том, как декораторы работают в python. Чтобы подготовиться к этому, давайте кратко рассмотрим *args и **kwargs

*арги и **кварги

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

def add_all_arguments(*args):
  result = 0
  for i in args:
    result += i
  return result
    
print(add_all_arguments(1,5,7,9,10))	# 32
print(add_all_arguments(1,9))	# 10
print(add_all_arguments(1,2,3,4,5,6,7,8,9,10))	# 55
print(add_all_arguments(1))	# 1
print(add_all_arguments())	# 0

Например, *args , **kwargs может принимать много аргументов, которые вы хотели бы ему предоставить. Однако **kwargs отличается от *args тем, что вам нужно будет назначить ключевые слова

def print_arguments(**kwargs):
  print(kwargs)
    
print(print_arguments(name = 'Moyosore'))	# {'name': 'moyosore'}
print(print_arguments(name = 'Moyosore'), country = 'Nigeria')
print(print_arguments())	# {}
# {'name': 'moyosore', 'country': 'Nigeria'}
 
def print_argument_values(**kwargs):
    for key, value in kwargs.items():
        print('{0}: {1}'.format(key, value))
 
print_argument_values(name="Moyosore", country="Nigeria")
# name: Moyosore
# country: Nigeria

Args и kwargs могут использоваться вместе в функции, причем args всегда предшествуют kwargs. Если есть какие-либо другие требуемые аргументы, они предшествуют args и kwargs

def add_and_mul(*args, **kwargs):
  pass
 
def add_and_mul(my_arg, *args, **kwargs):
  pass
 
def add_and_mul(my_arg, my_arg_1, *args, **kwargs):
  pass

Вы можете больше читать об args и кваргах для лучшего понимания. Это всего лишь пример для вас, чтобы иметь базовое представление об args и кваргах.

Следующий пост будет посвящен декораторам в деталях!