Какие ограничения имеют ограничения в Python по сравнению с закрытием языка X?
Где X - любой язык программирования (С#, Javascript, Lisp, Perl, Ruby, Scheme и т.д.), который поддерживает некоторый вкус закрытий.
Некоторые ограничения упоминаются в Closures in Python (по сравнению с закрытием Ruby), но статья старая, и многих ограничений не существует в современном Python.
Увидеть пример кода для конкретного ограничения было бы здорово.
Связанные вопросы:
Ответы
Ответ 1
Самое важное ограничение в настоящее время заключается в том, что вы не можете назначить переменную внешнего вида. Другими словами, закрытие доступно только для чтения:
>>> def outer(x):
... def inner_reads():
... # Will return outer '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 x first (and only) element.
... return x[0]
... def inner_writes(y):
... # Will look up outer 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
Ответ 2
Единственная трудность, с которой я столкнулась с Python в частности, - это когда они пытаются смешивать нефункциональные функции, такие как переназначение переменных с закрытием, и удивляются, когда это не работает:
def outer ():
x = 1
def inner ():
print x
x = 2
return inner
outer () ()
Обычно просто указывая на то, что функция имеет собственные локальные переменные, достаточно, чтобы сдержать такую глупость.
Ответ 3
Ограничение (или ограничение) закрытий на Python по сравнению с закрытием Javascript заключается в том, что он не может использоваться для эффективного скрытия данных
Javascript
var mksecretmaker = function(){
var secrets = [];
var mksecret = function() {
secrets.push(Math.random())
}
return mksecret
}
var secretmaker = mksecretmaker();
secretmaker(); secretmaker()
// privately generated secret number list
// is practically inaccessible
Python
import random
def mksecretmaker():
secrets = []
def mksecret():
secrets.append(random.random())
return mksecret
secretmaker = mksecretmaker()
secretmaker(); secretmaker()
# "secrets" are easily accessible,
# it difficult to hide something in Python:
secretmaker.__closure__[0].cell_contents # -> e.g. [0.680752847190161, 0.9068475951742101]
Ответ 4
Исправлено в Python 3 с помощью nonlocal
:
Оператор nonlocal
заставляет перечисленные идентификаторы ссылаться на ранее связанные переменные в ближайшей охватывающей области, исключая глобальные переменные. Это важно, потому что поведение по умолчанию для привязки - это сначала поиск в локальном пространстве имен. Этот оператор позволяет инкапсулированному коду перегруппировать переменные за пределами локальной области, кроме глобальной (модульной) области.
Ответ 5
@Джон Милликин
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
Ответ 6
комментарий для @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
Ответ 7
Лучшим обходным решением до 3.0 является включение переменной в качестве параметра по умолчанию в определение закрытой функции:
def f()
x = 5
def g(y, z, x=x):
x = x + 1