Какие ограничения имеют закрытия в Python по сравнению с языком X закрытий?

47
задан Community 23 May 2017 в 12:32
поделиться

6 ответов

Самое важное ограничение, в настоящее время, то, что Вы не можете присвоить переменной внешнего объема. Другими словами, закрытия только для чтения:

>>> def outer(x): 
...     def inner_reads():
...         # Will return outer's 'x'.
...         return x
...     def inner_writes(y):
...         # Will assign to a local 'x', not the outer 'x'
...         x = y
...     def inner_error(y):
...         # Will produce an error: 'x' is local because of the assignment,
...         # but we use it before it is assigned to.
...         tmp = x
...         x = y
...         return tmp
...     return inner_reads, inner_writes, inner_error
... 
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
5
>>> inner_error(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner_error
UnboundLocalError: local variable 'x' referenced before assignment

имя А, которому присваивают в локальном объеме (функция) всегда локально, если не объявлено иначе. В то время как существует 'глобальное' объявление для объявления переменной, глобальной, даже когда ему присваивают, нет такого объявления для вложенных переменных - еще. В Python 3.0, существует (будет), 'нелокальное' объявление, которое делает просто это.

можно работать вокруг этого ограничения тем временем при помощи изменяемого контейнерного типа:

>>> def outer(x):
...     x = [x]
...     def inner_reads():
...         # Will return outer's x's first (and only) element.
...         return x[0]
...     def inner_writes(y):
...         # Will look up outer's x, then mutate it.      
...         x[0] = y
...     def inner_error(y):
...         # Will now work, because 'x' is not assigned to, just referenced.
...         tmp = x[0]
...         x[0] = y
...         return tmp
...     return inner_reads, inner_writes, inner_error
... 
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
10
>>> inner_error(15)
10
>>> inner_reads()
15
44
ответ дан Thomas Wouters 26 November 2019 в 19:50
поделиться

Единственная трудность, с которой я видел, что люди встречаются с Python в особенности, состоит в том, когда они пытаются смешать нефункциональные функции как переменное переназначение с закрытиями и удивлены, когда это не работает:

def outer ():
    x = 1
    def inner ():
        print x
        x = 2
    return inner
outer () ()

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

6
ответ дан John Millikin 26 November 2019 в 19:50
поделиться

Зафиксированный в Python 3 через nonlocal оператор:

nonlocal оператор заставляет перечисленные идентификаторы относиться к ранее связанным переменным в самом близком объеме включения, исключая globals. Это важно, потому что поведение по умолчанию для привязки должно искать локальное пространство имен сначала. Оператор позволяет инкапсулировавшему коду снова переплетать переменные за пределами локального объема помимо глобального (модуль) объем.

4
ответ дан Cristian Ciupitu 26 November 2019 в 19:50
поделиться

Миллисемья @John

def outer():
    x = 1 # local to `outer()`

    def inner():
        x = 2     # local to `inner()`
        print(x)
        x = 3
        return x

    def inner2():
        nonlocal x
        print(x)  # local to `outer()`
        x = 4     # change `x`, it is not local to `inner2()`
        return x

    x = 5         # local to `outer()`
    return (inner, inner2)

for inner in outer():
    print(inner()) 

# -> 2 3 5 4
2
ответ дан jfs 26 November 2019 в 19:50
поделиться

комментарий для @Kevin Мало является ответом для включения примера кода

nonlocal, не решает полностью эту проблему на python3.0:

x = 0 # global x
def outer():
    x = 1 # local to `outer`
    def inner():
        global x
        x = 2 # change global
        print(x) 
        x = 3 # change global
        return x
    def inner2():
##        nonlocal x # can't use `nonlocal` here
        print(x)     # prints global
##        x = 4      # can't change `x` here
        return x
    x = 5
    return (inner, inner2)

for inner in outer():
    print(inner())
# -> 2 3 3 3

, С другой стороны:

x = 0
def outer():
    x = 1 # local to `outer`
    def inner():
##        global x
        x = 2
        print(x) # local to `inner` 
        x = 3 
        return x
    def inner2():
        nonlocal x
        print(x)
        x = 4  # local to `outer`
        return x
    x = 5
    return (inner, inner2)

for inner in outer():
    print(inner())
# -> 2 3 5 4

это работает над python3.1-3.3

2
ответ дан Community 26 November 2019 в 19:50
поделиться

Лучшим обходным путем до версии 3.0 является включение переменной в качестве параметра по умолчанию в прилагаемое определение функции:

def f()
    x = 5
    def g(y, z, x=x):
        x = x + 1
-2
ответ дан 26 November 2019 в 19:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: