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

Где X – любой язык программирования (C #, Javascript, Lisp, Perl, Ruby, Scheme и т. Д.), Который поддерживает некоторый вкус закрытий.

Некоторые ограничения упоминаются в Closures in Python (по сравнению с закрытием Ruby), но статья старая, и многие ограничения уже не существуют в современном Python.

Увидеть пример кода для конкретного ограничения было бы здорово.

Связанные вопросы :

  • Можете ли вы объяснить закрытие (как они относятся к Python)?
  • Что такое «Закрытие»?
  • Как работает закрытие javascript?

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

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

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

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

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

Ограничение (или ограничение) закрытия 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 

питон

 import random def mksecretmaker(): secrets = [] def mksecret(): secrets.append(random.random()) return mksecret secretmaker = mksecretmaker() secretmaker(); secretmaker() # "secrets" are easily accessible, # it's difficult to hide something in Python: secretmaker.__closure__[0].cell_contents # -> eg [0.680752847190161, 0.9068475951742101] 

Исправлено в Python 3 через nonlocal утверждение:

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

@ Джон Милликин

 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 

комментарий для ответа @Kevin Little, чтобы включить пример кода

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

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

 def f ()
     x = 5
     def g (y, z, x = x):
         x = x + 1