221 votes

Est-il possible de modifier une variable en python qui est dans une portée externe, mais pas globale ?

Le code suivant est donné :

def A() :
    b = 1

    def B() :
        # I can access 'b' from here.
        print( b )
        # But can i modify 'b' here? 'global' and assignment will not work.

    B()
A()

Pour le code dans B() fonction variable b est dans la portée externe, mais pas dans la portée globale. Est-il possible de modifier b variable de l'intérieur B() fonction ? Je peux sûrement le lire d'ici et print() mais comment le modifier ?

243voto

Adam Wagner Points 7232

Python 3.x dispose de la fonction nonlocal mot-clé . Je pense que cela fait ce que vous voulez, mais je ne sais pas si vous utilisez python 2 ou 3.

L'instruction non locale fait en sorte que les identificateurs listés se réfèrent à des à des variables précédemment liées dans l'étendue la plus proche. Ceci est important car le comportement par défaut pour la liaison est de rechercher d'abord dans l'espace de nom local en premier. L'instruction permet au code encapsulé de de lier à nouveau des variables en dehors de l'espace local, en plus de l'espace global (module). (module).

Pour python 2, je me contente généralement d'utiliser un objet mutable (comme une liste ou un dict), et de muter la valeur au lieu de la réaffecter.

exemple :

def foo():
    a = []
    def bar():
        a.append(1)
    bar()
    bar()
    print a

foo()

Sorties :

[1, 1]

28voto

chrisk Points 11

Vous pouvez utiliser une classe vide pour contenir une portée temporaire. C'est comme le mutable mais un peu plus joli.

def outer_fn():
   class FnScope:
     b = 5
     c = 6
   def inner_fn():
      FnScope.b += 1
      FnScope.c += FnScope.b
   inner_fn()
   inner_fn()
   inner_fn()

Cela donne la sortie interactive suivante :

>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined

15voto

Mike Edwards Points 2609

Je suis un peu novice en matière de Python, mais j'ai lu un peu sur ce sujet. Je pense que le mieux que vous puissiez obtenir est similaire à la solution Java, qui consiste à envelopper votre variable externe dans une liste.

def A():
   b = [1]
   def B():
      b[0] = 2
   B()
   print(b[0])

# The output is '2'

Edit : Je suppose que c'était probablement vrai avant Python 3. On dirait que nonlocal est votre réponse.

1voto

zchenah Points 938

Non, vous ne pouvez pas, du moins de cette manière.

Parce que l'opération "set" créera un nouveau nom dans l'étendue actuelle, qui couvre l'étendue extérieure.

1voto

eyquem Points 9942

Je ne sais pas s'il existe un attribut d'une fonction qui donne la __dict__ de l'espace extérieur de la fonction lorsque cet espace extérieur n'est pas l'espace global == le module, ce qui est le cas lorsque la fonction est une fonction imbriquée, en Python 3.

Mais dans Python 2, pour autant que je sache, un tel attribut n'existe pas.

Donc les seules possibilités de faire ce que vous voulez sont :

1) utiliser un objet mutable, comme l'ont dit d'autres personnes

2)

def A() :
    b = 1
    print 'b before B() ==', b

    def B() :
        b = 10
        print 'b ==', b
        return b

    b = B()
    print 'b after B() ==', b

A()

résultat

b before B() == 1
b == 10
b after B() == 10

.

Nota

La solution de Cédric Julien présente un inconvénient :

def A() :
    global b # N1
    b = 1
    print '   b in function B before executing C() :', b

    def B() :
        global b # N2
        print '     b in function B before assigning b = 2 :', b
        b = 2
        print '     b in function B after  assigning b = 2 :', b

    B()
    print '   b in function A , after execution of B()', b

b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b

résultat

global b , before execution of A() : 450
   b in function B before executing B() : 1
     b in function B before assigning b = 2 : 1
     b in function B after  assigning b = 2 : 2
   b in function A , after execution of B() 2
global b , after execution of A() : 2

Le marché mondial b après l'exécution de A() a été modifié et il se peut qu'il ne soit pas souhaité ainsi

C'est le cas seulement s'il y a un objet avec l'identifiant b dans l'espace de nom global

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