Pull to refresh

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()

А вот если используется wraps все гораздо проще :)

Ребят, ваша проблема в том, что прежде чем начинать что-то объяснять, надо определить проблему. Нужно начинать с того - зачем это нужно и почему, а потом уже рассказывать, как оно работает и как устроено.

Полностью согласен, больше практических примеров. Особенно сравнительных, ну типа вот если без замыкания то вот 200 строк, с замыканием 20.

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

Я не думаю, что есть технические ограничения в кол-во вложений, т.к. "глубина" определяется в момента определения функции, а не в момент выполнения, как с рекурсией.
С другой стороны, нужно писать понятный код, потому что его кому-то придется поддерживать. 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... И этот контекст может добавляться бесконечно долго, а любая разработка превращается в последовательность компромиссов. Ведь уже есть какие-то наработки, их нельзя просто выкинуть, кто-то за это уже заплатил, задачи, которых накинули на ближайшие пару лет, а единственная наша свобода — это расстановка приоритетов... Ну и главное, учитывая, что каждый год создаются новые библиотеки, фреймворки, языки программирования, которые нужно не просто изучать и знать, но и разбираться в том, в каких случаях что применять лучше, разработка будет становиться только сложнее.
Во-вторых, что касается статьи. Тот же счетчик можно было бы реализовать с помощью классов, и думается мне, что это решение не уступило бы по эффективности. Но задача статьи не продемонстрировать крутость и эффективность замыканий, или агитировать за их использование в проде. Её задача - дать читателю базу для знакомства с декораторами и только в этом контексте её стоит рассматривать.

Sign up to leave a comment.

Articles