tipos - Cierre de Python: escribir en la variable en el alcance principal
variables globales python (6)
¿Qué pasa con el uso de una instancia de clase para contener el estado? Instancias una clase y pasa los métodos de instancia a los subs y esas funciones tendrían una referencia a uno mismo ...
Tengo el siguiente código dentro de una función:
stored_blocks = {}
def replace_blocks(m):
block = m.group(0)
block_hash = sha1(block)
stored_blocks[block_hash] = block
return ''{{{%s}}}'' % block_hash
num_converted = 0
def convert_variables(m):
name = m.group(1)
num_converted += 1
return ''<%%= %s %%>'' % name
fixed = MATCH_DECLARE_NEW.sub('''', template)
fixed = MATCH_PYTHON_BLOCK.sub(replace_blocks, fixed)
fixed = MATCH_FORMAT.sub(convert_variables, fixed)
Agregar elementos a stored_blocks
funciona bien, pero no puedo aumentar num_converted
en la segunda subfunción:
UnboundLocalError: variable local ''num_converted'' a la que se hace referencia antes de la asignación
Podría usar variables global
pero las globales son feas y realmente no necesito que esa variable sea global en absoluto.
Entonces, tengo curiosidad de cómo puedo escribir en una variable en el alcance de la función principal. nonlocal num_converted
probablemente haga el trabajo, pero necesito una solución que funcione con Python 2.x.
(ver a continuación la respuesta editada)
Puedes usar algo como:
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted += 1
return ''<%%= %s %%>'' % name
convert_variables.num_converted = 0
De esta forma, num_converted
funciona como una variable "estática" similar a C del método convert_variable
(editado)
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1
return ''<%%= %s %%>'' % name
De esta forma, no necesita inicializar el contador en el procedimiento principal.
Modificado de: https://.com/a/40690954/819544
Puede aprovechar el módulo de inspect
para acceder al dict global de scope scope y escribir en eso. Eso significa que este truco incluso se puede aprovechar para acceder al alcance de la llamada desde una función anidada definida en un submódulo importado.
import inspect
def get_globals(scope_level=0):
return dict(inspect.getmembers(inspect.stack()[scope_level][0]))["f_globals"]
num_converted = 0
def foobar():
get_globals(0)[''num_converted''] += 1
foobar()
print(num_converted)
# 1
Juega con el argumento scope_level
según sea necesario. Setting scope_level=1
funciona para una función definida en un submódulo, scope_level=2
para la función interna definida en un decorador en un submódulo, etc.
NB: Solo porque puedas hacer esto, no significa que debas hacerlo.
Tengo un par de comentarios.
En primer lugar, aparece una aplicación para tales funciones anidadas cuando se trata de devoluciones de llamada sin formato, como se usan en bibliotecas como xml.parsers.expat. (Que los autores de la biblioteca eligieron este enfoque puede ser objetable, pero ... hay razones para usarlo de todos modos).
Segundo: dentro de una clase, hay alternativas mucho mejores a la matriz (num_converted [0]). Supongo que esto es de lo que Sebastjan estaba hablando.
class MainClass:
_num_converted = 0
def outer_method( self ):
def convert_variables(m):
name = m.group(1)
self._num_converted += 1
return ''<%%= %s %%>'' % name
Todavía es lo suficientemente extraño como para merecer un comentario en el código ... Pero la variable es al menos local para la clase.
Usar la palabra clave global
está bien. Si tú escribes:
num_converted = 0
def convert_variables(m):
global num_converted
name = m.group(1)
num_converted += 1
return ''<%%= %s %%>'' % name
... num_converted
no se convierte en una "variable global" (es decir, no se vuelve visible en ningún otro lugar inesperado), solo significa que se puede modificar dentro de convert_variables
. Eso parece ser exactamente lo que quieres.
Para decirlo de otra manera, num_converted
ya es una variable global. Todo lo que hace la sintaxis global num_converted
es decirle a Python "dentro de esta función, no crear una variable num_converted
local, en su lugar, usar la global existente".
Problema: Esto se debe a que las reglas de alcance de Python están dementes. La presencia del operador de asignación +=
marca el objetivo, num_converted
, como local para el alcance de la función num_converted
, y no hay forma de sonido en Python 2.x para acceder a un solo nivel de alcance desde allí. Solo la palabra clave global
puede sacar las referencias de variables del alcance actual y lo lleva directamente a la parte superior.
Solución: convierte num_converted
en una matriz de un solo elemento.
num_converted = [0]
def convert_variables(m):
name = m.group(1)
num_converted[0] += 1
return ''<%%= %s %%>'' % name