78 votes

Python fermeture: Écrire à la variable dans la portée parent

J'ai le code suivant à l'intérieur d'une fonction:

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)

Ajouter des éléments à l' stored_blocks fonctionne bien, mais je ne peut pas augmenter num_converted dans le deuxième sous-fonction:

UnboundLocalError: la variable locale 'num_converted' référencé avant affectation

Je pourrais utiliser global , mais les variables globales sont moches et je n'ai vraiment pas besoin de cette variable globale.

Donc, je suis curieux de voir comment je peux écrire une variable dans la fonction parent portée. nonlocal num_converted serait probablement faire le travail, mais j'ai besoin d'une solution qui fonctionne avec Python 2.x.

88voto

Marcelo Cantos Points 91211

Tourner num_converted en un unique élément de tableau.

num_converted = [0]
def convert_variables(m):
    name = m.group(1)
    num_converted[0] += 1
    return '<%%= %s %%>' % name

29voto

PabloG Points 9308

(voir ci-dessous pour les édité réponse)

Vous pouvez utiliser quelque chose comme:

def convert_variables(m):
    name = m.group(1)
    convert_variables.num_converted += 1
    return '<%%= %s %%>' % name

convert_variables.num_converted = 0

De cette façon, num_converted fonctionne comme un C-like "statique" de la variable de la méthode convert_variable


(édité)

def convert_variables(m):
    name = m.group(1)
    convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1
    return '<%%= %s %%>' % name

De cette façon, vous n'avez pas besoin d'initialiser le compteur dans la procédure principale.

9voto

Emile Points 617

À l'aide de l' global mot-clé est très bien. Si vous écrivez:

num_converted = 0
def convert_variables(m):
    global num_converted
    name = m.group(1)
    num_converted += 1
    return '<%%= %s %%>' % name

... num_converted ne devienne pas une "variable globale" (c'est à dire qu'il ne devient pas visible dans tous les autres lieux inattendus), cela signifie simplement qu'il peut être modifié à l'intérieur d' convert_variables. Qui semble être exactement ce que vous voulez.

Pour le dire d'une autre façon, num_converted est déjà une variable globale. Tous les global num_converted syntaxe n'est de dire à Python "à l'intérieur de cette fonction, de ne pas créer un local num_converted variable, au lieu de cela, utiliser le mondial.

7voto

Seb Points 5120

Que penser de l'utilisation d'une instance de classe à tenir l'état? Vous instancier une classe et passer les méthodes d'instance de sous-marins et les fonctions qui aurait une référence à soi-même...

6voto

Steve White Points 106

J'ai quelques remarques.

Tout d'abord, une demande pour de telles fonctions imbriquées vient lorsque l'on traite avec des rappels, comme sont utilisés dans les bibliothèques comme xml.les parseurs.les expatriés. (La bibliothèque des auteurs choisi cette approche peut être choquant, mais ... il y a des raisons de l'utiliser tout de même.)

Deuxième: au sein d'une classe, il y a beaucoup plus agréable des solutions de rechange à la matrice (num_converted[0]). Je suppose que c'est ce que Sebastjan était en train de parler.

class MainClass:
    _num_converted = 0
    def outer_method( self ):
        def convert_variables(m):
            name = m.group(1)
            self._num_converted += 1
            return '<%%= %s %%>' % name

C'est toujours bizarre assez pour mériter un commentaire dans le code... Mais la variable est local au moins à la classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X