Comments 17
Мне пригодился nonlocal только один раз, когда делал хитровымудренный декоратор на троттлинг (ограничение частоты вызовов) асинхронной функции. Заодно понял, почему nonlocal-переменные - это private для бедных. Классом, наверно, было бы проще.
Здравствуйте! Большое спасибо за статью, как раз разбираюсь с функциями, как и что туда передается.
Методом "научного тыка" обнаружила следующее. Если в функцию не передаём параметры, то конфликт имён (глобальная и локальная переменная с одним именем) вызывает ошибку. Если же глобальная переменная передается через параметр, а затем с именем параметра об'является локальная - никакого конфликта нет. Как-то нелогично.
Может быть, где-то можно про это прочитать, в чем профит?
Добрый день!
Не совсем понял о какой ошибке речь. Может проблема в том что вы пытаетесь изменить значение переменной, до того как ей будет что-то присвоено?
a = 5
def foo():
a = 10
print(a)
foo() # 10
Этот код выполнится, не смотря на то что имена локальной и глобальной переменной совпадают. Если же вы попробуете внутри функции изменить значение переменной, например `a += 10` тогда возникнет ошибка, т.к. интерпретатор не видит в локальной области видимости переменную с именем a
. Что бы не сталкиваться с ней, я вижу 3 пути: первый, плохой, объявить внутри функции `global a`. Второй ваш, передать функции переменную в качестве аргумента. Третий, использовать внутри переменную с другим именем.
a = 5
def foo():
b = a + 10
print(b)
foo()
Можно вечно смотреть на 3 вещи:
- огонь
- воду
- и как объясняют концепцию декораторов для питона
Декоратор кто угодно напишет. А вот когда просишь рандомного питониста сделать обратную операцию - "раздекорировать" декорированную функцию, он, обычно, впадает в ступор.
*впал в ступор* (к) рандомный питонист.
Но если подумать, то, насколько помню, есть атрибут, который содержит порядок применения декораторов. Если не изменяет память, то он содержит оригинальные объекты функций, но это совсем не точно, даже интересно почитать доку на этот счёт
Для тех, кто впал в ступор и всё ещё не выпал:
import inspect
from types import FunctionType
def decorator(func):
def wrapper():
print("before")
func()
print("after")
return wrapper
@decorator
@decorator
def my_function():
print("func")
print("Decorated:")
my_function()
def extract_wrapped(decorated):
closure = decorated.__closure__
if closure:
for cell in closure:
if isinstance(cell.cell_contents, FunctionType) and cell.cell_contents.__closure__ is None:
return cell.cell_contents
else:
return extract_wrapped(cell.cell_contents)
return None
original = extract_wrapped(my_function)
print("\nOriginal source:")
print(inspect.getsource(original))
print("\nOriginal:")
original()
Ребят, ваша проблема в том, что прежде чем начинать что-то объяснять, надо определить проблему. Нужно начинать с того - зачем это нужно и почему, а потом уже рассказывать, как оно работает и как устроено.
"Однако важно отметить, что существует множество ситуаций, в которых от использования замыканий отказаться нельзя" -- для меня такой пример это написание параметризованных фикстур для pytest.
А сколько максимально вложений может быть def def def ?
Я не думаю, что есть технические ограничения в кол-во вложений, т.к. "глубина" определяется в момента определения функции, а не в момент выполнения, как с рекурсией.
С другой стороны, нужно писать понятный код, потому что его кому-то придется поддерживать. 3 уровня замыканий используются в декораторах с параметрами. Большее кол-во уровней вложений мне сейчас в голову не приходит.
Но это работает
def create_closure(a):
def level_two(b):
def level_three(c):
def level_four(d):
def level_five(e):
def level_six(f):
def level_seven(g):
def level_eight(h):
def level_nine(i):
def level_ten(j):
def level_eleven(k):
def level_twelve(l):
def level_thirteen(m):
def level_fourteen(n):
def level_fifteen(o):
def level_sixteen(p):
def level_seventeen(q):
def level_eighteen(r):
def level_nineteen(s):
def level_twenty(t):
def level_twenty_one(u):
def level_twenty_two(v):
def level_twenty_three(w):
def level_twenty_four(x):
def level_twenty_five(y):
return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y
return level_twenty_five
return level_twenty_four
return level_twenty_three
return level_twenty_two
return level_twenty_one
return level_twenty
return level_nineteen
return level_eighteen
return level_seventeen
return level_sixteen
return level_fifteen
return level_fourteen
return level_thirteen
return level_twelve
return level_eleven
return level_ten
return level_nine
return level_eight
return level_seven
return level_six
return level_five
return level_four
return level_three
return level_two
result = create_closure(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)(21)(22)(23)(24)(25)
print(result)
спасибо чатгпт, что мне не нужно писать это руками
Сразу оговорюсь я не программист, а бизнес аналитик. Да и мой комментарий скорее к коментам выше, чем к самой статье. Но мне как не программисту не вполне понятно, для чего программисты сначала создают себе проблемы, а потом героически их решают. Каждый год создаются новые библиотеки, фреймворки, языки программирования, но сама разработка не становится проще. Замыкание прекрасная концепция из функционального если не ошибаюсь программирования, но когда ее применяют для неявной модификации стейта, что способно взорвать мозг при последующей отладке, то в голове только один вопрос - зачем? Действительно хотелось бы видеть в статье адекватные примеры реализации. Скажем не просто счетчик, но и объяснение, почему другим образом его реализовывать не эффективно. Мне кажется такое объяснение позволит избежать применения той или иной технологии не к месту и не по назначению.
Спасибо за комментарий. Пришлось подумать о том, как лучше ответить.
Во-первых, про простоту и сложность. Тут, мне кажется, важным является именно контекст. Любые вопросы кажутся простыми, когда мы пытаемся рассматривать их в вакууме. Можем ли мы реализовать программу на Python? Конечно. Но вот мы добавляем контекст о ограничении ресурсов, и кажется, что лучшим выбором становится C. Но потом оказывается, что у нас уже есть команда, но специализируется она на Java... И этот контекст может добавляться бесконечно долго, а любая разработка превращается в последовательность компромиссов. Ведь уже есть какие-то наработки, их нельзя просто выкинуть, кто-то за это уже заплатил, задачи, которых накинули на ближайшие пару лет, а единственная наша свобода — это расстановка приоритетов... Ну и главное, учитывая, что каждый год создаются новые библиотеки, фреймворки, языки программирования, которые нужно не просто изучать и знать, но и разбираться в том, в каких случаях что применять лучше, разработка будет становиться только сложнее.
Во-вторых, что касается статьи. Тот же счетчик можно было бы реализовать с помощью классов, и думается мне, что это решение не уступило бы по эффективности. Но задача статьи не продемонстрировать крутость и эффективность замыканий, или агитировать за их использование в проде. Её задача - дать читателю базу для знакомства с декораторами и только в этом контексте её стоит рассматривать.
Замыкания и декораторы в Python: часть 1 — замыкания