Recherche…


Syntaxe

  • global a, b, c
  • non local a, b
  • x = quelque chose # lie x
  • (x, y) = quelque chose # lie x et y
  • x + = quelque chose # lie x. De même pour tous les autres "op ="
  • del x # lie x
  • pour x dans quelque chose: # lie x
  • avec quelque chose comme x: # lie x
  • sauf exception comme ex: # lie ex à l'intérieur du bloc

Variables globales

Dans Python, les variables à l'intérieur des fonctions sont considérées comme locales si et seulement si elles apparaissent dans la partie gauche d'une instruction d'affectation ou dans une autre occurrence de liaison. sinon, une telle liaison est recherchée dans des fonctions englobantes, jusqu'au niveau global. Cela est vrai même si l'instruction d'affectation n'est jamais exécutée.

x = 'Hi'

def read_x():
    print(x)   # x is just referenced, therefore assumed global

read_x()       # prints Hi

def read_y():
    print(y)   # here y is just referenced, therefore assumed global

read_y()       # NameError: global name 'y' is not defined

def read_y():
    y = 'Hey'  # y appears in an assignment, therefore it's local
    print(y)   # will find the local y

read_y()       # prints Hey

def read_x_local_fail():
    if False:
        x = 'Hey'  # x appears in an assignment, therefore it's local
    print(x)   # will look for the _local_ z, which is not assigned, and will not be found

read_x_local_fail()   # UnboundLocalError: local variable 'x' referenced before assignment

Normalement, une affectation à l'intérieur d'une étendue ombrera les variables externes du même nom:

x = 'Hi'

def change_local_x():
    x = 'Bye'
    print(x)
change_local_x()  # prints Bye
print(x)  # prints Hi

Déclarer un nom global signifie que, pour le reste de la portée, toute affectation au nom se produira au niveau supérieur du module:

x = 'Hi'

def change_global_x():
    global x
    x = 'Bye'
    print(x)

change_global_x()  # prints Bye
print(x)  # prints Bye

Le mot-clé global signifie que les affectations auront lieu au niveau supérieur du module, et non au niveau supérieur du programme. Les autres modules nécessiteront toujours l'accès habituel aux variables du module.

En résumé: pour savoir si une variable x est locale à une fonction, vous devez lire l' intégralité de la fonction:

  1. si vous avez trouvé global x , alors x est une variable globale
  2. Si vous avez trouvé un nonlocal x , alors x appartient à une fonction englobante et n'est ni local ni global
  3. Si vous avez trouvé x = 5 ou for x in range(3) ou une autre liaison, alors x est une variable locale
  4. Sinon, x appartient à une étendue englobante (étendue de la fonction, étendue globale ou fonctions intégrées).

Variables locales

Si un nom est lié à l' intérieur d'une fonction, il est accessible par défaut uniquement dans la fonction:

def foo():
    a = 5
    print(a) # ok

print(a) #  NameError: name 'a' is not defined

Les constructions de flux de contrôle n'ont aucun impact sur la portée (à l'exception de except ), mais l'accès à la variable qui n'a pas encore été attribué est une erreur:

def foo():
    if True: 
        a = 5
    print(a) # ok

b = 3
def bar():
    if False:
        b = 5
    print(b) # UnboundLocalError: local variable 'b' referenced before assignment

Les opérations de liaison communes sont des affectations, for boucles et des affectations augmentées telles que a += 5

Variables non locales

Python 3.x 3.0

Python 3 a ajouté un nouveau mot-clé appelé nonlocal . Le mot clé nonlocal ajoute un remplacement de portée à la portée interne. Vous pouvez tout lire à ce sujet dans PEP 3104 . Ceci est mieux illustré avec quelques exemples de code. L'un des exemples les plus courants est de créer une fonction pouvant s'incrémenter:

def counter():
    num = 0
    def incrementer():
        num += 1
        return num
    return incrementer

Si vous essayez d'exécuter ce code, vous recevrez une erreur UnboundLocalError car la variable num est référencée avant d'être affectée dans la fonction la plus interne. Ajoutons nonlocal au mixage:

def counter():
    num = 0
    def incrementer():
        nonlocal num
        num += 1
        return num
    return incrementer

c = counter()
c() # = 1
c() # = 2
c() # = 3

Fondamentalement, nonlocal - nonlocal vous permettra d'attribuer des variables dans une portée externe, mais pas une portée globale. Donc, vous ne pouvez pas utiliser nonlocal dans notre fonction de counter , car cela tenterait d’attribuer une portée globale. Essayez-le et vous obtiendrez rapidement une SyntaxError . Au lieu de cela, vous devez utiliser nonlocal dans une fonction imbriquée.

(Notez que la fonctionnalité présentée ici est mieux implémentée en utilisant des générateurs.)

Occurrence de liaison

x = 5
x += 7
for x in iterable: pass    

Chacune des instructions ci-dessus est une occurrence de liaison - x devient lié à l’objet désigné par 5 . Si cette instruction apparaît dans une fonction, alors x sera la fonction locale par défaut. Voir la section "Syntaxe" pour une liste des instructions de liaison.

Les fonctions ignorent la portée de la classe lors de la recherche de noms

Les classes ont une portée locale lors de la définition, mais les fonctions de la classe n'utilisent pas cette étendue lors de la recherche de noms. Comme les lambdas sont des fonctions et que les compréhensions sont implémentées à l'aide de la portée des fonctions, cela peut entraîner un comportement surprenant.

a = 'global'

class Fred:
    a = 'class'  # class scope
    b = (a for i in range(10))  # function scope
    c = [a for i in range(10)]  # function scope
    d = a  # class scope
    e = lambda: a  # function scope
    f = lambda a=a: a  # default argument uses class scope
    
    @staticmethod  # or @classmethod, or regular instance method
    def g():  # function scope
        return a

print(Fred.a)  # class
print(next(Fred.b))  # global
print(Fred.c[0])  # class in Python 2, global in Python 3
print(Fred.d)  # class
print(Fred.e())  # global
print(Fred.f())  # class
print(Fred.g()) # global

Les utilisateurs peu familiarisés avec le fonctionnement de cette étendue peuvent s'attendre à ce que b , c et e impriment la class .


De PEP 227 :

Les noms dans la portée de la classe ne sont pas accessibles. Les noms sont résolus dans la portée de la fonction la plus interne. Si une définition de classe se produit dans une chaîne de portées imbriquées, le processus de résolution ignore les définitions de classe.

De la documentation de Python sur la dénomination et la liaison :

La portée des noms définis dans un bloc de classe est limitée au bloc de classe; il ne s'étend pas aux blocs de code des méthodes - cela inclut les expressions de compréhension et de générateur car elles sont implémentées en utilisant une étendue de fonction. Cela signifie que les éléments suivants échoueront:

class A:
    a = 42
    b = list(a + i for i in range(10))

Cet exemple utilise les références de cette réponse de Martijn Pieters, qui contient une analyse plus approfondie de ce comportement.

La commande del

Cette commande a plusieurs formes liées mais distinctes.

del v

Si v est une variable, la commande del v supprime la variable de son étendue. Par exemple:

x = 5
print(x) # out: 5
del x
print(x) # NameError: name 'f' is not defined

Notez que del est une occurrence de liaison , ce qui signifie que, sauf indication contraire explicite (en utilisant des nonlocal ou global ), del v affectera v local à la portée actuelle. Si vous envisagez de supprimer v dans une étendue externe, utilisez nonlocal v ou global v dans la même portée de l'instruction del v .

Dans tout ce qui suit, l’intention d’une commande est un comportement par défaut mais n’est pas appliquée par le langage. Une classe peut être écrite de manière à invalider cette intention.

del v.name

Cette commande déclenche un appel à la v.__delattr__(name) .

L'intention est de rendre le name attribut indisponible. Par exemple:

class A:
    pass

a = A()
a.x = 7
print(a.x) # out: 7
del a.x
print(a.x) # error: AttributeError: 'A' object has no attribute 'x'

del v[item]

Cette commande déclenche un appel à v.__delitem__(item) .

L'intention est que cet item n'appartienne pas au mappage implémenté par l'objet v . Par exemple:

x = {'a': 1, 'b': 2}
del x['a']
print(x) #  out: {'b': 2}
print(x['a']) # error: KeyError: 'a'

del v[a:b]

Cela appelle réellement v.__delslice__(a, b) .

L'intention est similaire à celle décrite ci-dessus, mais avec des tranches - des plages d'éléments au lieu d'un seul élément. Par exemple:

x = [0, 1, 2, 3, 4]
del x[1:3]
print(x) #  out: [0, 3, 4]

Voir aussi Garbage Collection # La commande del

Portée locale vs globale

Quelle est la portée locale et globale?

Toutes les variables Python accessibles à un moment donné dans le code sont de portée locale ou globale .

L'explication est que la portée locale inclut toutes les variables définies dans la fonction en cours et que la portée globale inclut les variables définies en dehors de la fonction en cours.

foo = 1  # global

def func():
    bar = 2  # local
    print(foo)  # prints variable foo from global scope
    print(bar)  # prints variable bar from local scope

On peut inspecter quelles variables sont dans quelle portée. Les fonctions intégrées locals() et globals() renvoient l'ensemble des étendues en tant que dictionnaires.

foo = 1

def func():
    bar = 2
    print(globals().keys())  # prints all variable names in global scope
    print(locals().keys())  # prints all variable names in local scope

Que se passe-t-il avec les conflits de noms?

foo = 1

def func():
    foo = 2  # creates a new variable foo in local scope, global foo is not affected

    print(foo)  # prints 2

    # global variable foo still exists, unchanged:
    print(globals()['foo'])  # prints 1
    print(locals()['foo'])  # prints 2

Pour modifier une variable globale, utilisez le mot-clé global :

foo = 1

def func():
    global foo
    foo = 2  # this modifies the global foo, rather than creating a local variable

La portée est définie pour tout le corps de la fonction!

Cela signifie qu'une variable ne sera jamais globale pour la moitié de la fonction et locale par la suite, ou inversement.

foo = 1

def func():
    # This function has a local variable foo, because it is defined down below.
    # So, foo is local from this point. Global foo is hidden.

    print(foo) # raises UnboundLocalError, because local foo is not yet initialized
    foo = 7
    print(foo)

De même, l'opposite:

foo = 1

def func():
    # In this function, foo is a global variable from the begining

    foo = 7  # global foo is modified

    print(foo)  # 7
    print(globals()['foo'])  # 7

    global foo  # this could be anywhere within the function
    print(foo)  # 7

Fonctions dans les fonctions

Il peut exister de nombreux niveaux de fonctions imbriquées dans les fonctions, mais au sein d’une même fonction, il n’ya qu’une seule portée locale pour cette fonction et la portée globale. Il n'y a pas de portées intermédiaires.

foo = 1

def f1():
    bar = 1

    def f2():
        baz = 2
        # here, foo is a global variable, baz is a local variable
        # bar is not in either scope
        print(locals().keys())  # ['baz']
        print('bar' in locals())  # False
        print('bar' in globals())  # False

    def f3():
        baz = 3
        print(bar)  # bar from f1 is referenced so it enters local scope of f3 (closure)
        print(locals().keys())  # ['bar', 'baz']
        print('bar' in locals())  # True
        print('bar' in globals())  # False

    def f4():
        bar = 4  # a new local bar which hides bar from local scope of f1
        baz = 4
        print(bar)
        print(locals().keys())  # ['bar', 'baz']
        print('bar' in locals())  # True
        print('bar' in globals())  # False

global vs nonlocal (Python 3 uniquement)

Ces deux mots-clés sont utilisés pour obtenir un accès en écriture aux variables qui ne sont pas locales pour les fonctions en cours.

Le mot-clé global déclare qu'un nom doit être traité comme une variable globale.

foo = 0  # global foo

def f1():
    foo = 1  # a new foo local in f1
    
    def f2():
        foo = 2  # a new foo local in f2
        
        def f3():
            foo = 3  # a new foo local in f3
            print(foo)  # 3
            foo = 30  # modifies local foo in f3 only
        
        def f4():
            global foo
            print(foo)  # 0
            foo = 100  # modifies global foo

D'un autre côté, nonlocal (voir Variables nonlocal ), disponible dans Python 3, prend une variable locale d'une portée englobante dans la portée locale de la fonction actuelle.

De la documentation Python sur nonlocal :

L'instruction nonlocal fait que les identificateurs répertoriés font référence aux variables précédemment liées dans la portée englobante la plus proche, à l'exception des globales.

Python 3.x 3.0
def f1():
    
    def f2():
        foo = 2  # a new foo local in f2

        def f3():
            nonlocal foo  # foo from f2, which is the nearest enclosing scope
            print(foo)  # 2
            foo = 20  # modifies foo from f2!


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow