Python >> Tutoriel Python >  >> Python Tag >> exec()

Comment importer des bibliothèques dans la fonction exec() de Python ?

Qu'est-ce que la fonction exec()

exec() est une fonction Python intégrée qui est le plus souvent utilisée pour exécuter dynamiquement du code, sous forme de chaîne ou de code objet. Pour bien comprendre comment nous pouvons utiliser exec() pour importer des bibliothèques et des modules, nous devons nous familiariser avec la syntaxe de la fonction elle-même, car elle deviendra pertinente plus tard :

exec(object, globals, locals)

Comme vous pouvez le voir, il a trois paramètres qui peuvent être définis comme suit :

  • object :la chaîne ou le code objet à exécuter
  • globals  :un dictionnaire des méthodes et variables globales disponibles (optionnel)
  • locals  :un dictionnaire des méthodes et variables locales disponibles (optionnel)

Utilisation de base

Voyons maintenant comment exec() peut être utilisé pour exécuter dynamiquement du code dans sa forme la plus élémentaire, soit sous forme de chaîne, soit sous forme de code objet :

program = 'a = 5; b=5; print("a x b  =", a*b)'
exec(program)
a x b = 25

Par défaut exec() est livré avec un ensemble de fonctions intégrées pouvant être exécutées sans avoir à importer, que nous pouvons lister en imprimant le répertoire comme suit :

exec(print(dir()))
# ['In', 'Out', '_', '__', '___', '__builtin__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_dh', '_i', '_i1', '_ih', '_ii', '_iii', '_oh', 'exit', 'get_ipython', 'quit']

Et si nous voulons exec() faire quelque chose en dehors de ces bibliothèques par défaut ? Pouvons-nous importer des bibliothèques et les exécuter dynamiquement aussi ? Vous serez heureux de savoir que la réponse courte est oui ! Pour le démontrer, prenons l'exemple du datetime module qui est une bibliothèque standard Python - donc même si rien n'a besoin d'être téléchargé, il doit être importé pour s'exécuter.

Importer le module dans la chaîne de code

Le moyen le plus simple d'importer une bibliothèque consiste à inclure l'instruction d'importation dans notre chaîne ou code objet et à la transmettre via la méthode exec() :

program = '''
import datetime
print(datetime.datetime.now())
'''
exec(program)
# 2021-01-25 12:22:58.680938

Quand on appelle le exec() fonction, il lit chaque ligne de code et s'exécute, de sorte que l'instruction d'importation de base fonctionne dans exec( ) comme d'habitude. Avec le code ci-dessus, nous disons essentiellement :

exec(import datetime; print(datetime.datetime.now())

Nous pouvons confirmer que le datetime module a bien été importé en vérifiant les bibliothèques qui sont maintenant disponibles dans le exec() répertoire :

exec(print(dir()))
# ['In', 'Out', …. 'datetime', 'exit', 'get_ipython', 'program', 'quit']

Comme datetime fait maintenant partie du exec() dictionnaire il peut être utilisé par le exec() fonctionnent par défaut n'importe où ailleurs dans votre code sans avoir à être réimportés.

Si nous exécutons ensuite :

prog = '''
print(datetime.datetime.now())
'''
exec(prog)
# 2021-01-25 12:23:53.207181

Le code s'exécute même si nous n'avons pas explicitement demandé exec() pour importer le module.

Maintenant, que se passerait-il si nous voulions importer le datetime module puis l'appeler dans une portée différente, par exemple une fonction, peut exec() gérer ça? Jetons un coup d'œil :

program = '''
import datetime
def f():
    print(datetime.datetime.now())
f()
'''
exec(program)
# 2021-01-25 12:26:49.306432

Alors oui, c'est aussi possible et nous pouvons confirmer que l'importation a fonctionné en imprimant le exec() répertoire :

exec(print(dir()))
# ['In', 'Out', …. 'datetime', 'exit', 'get_ipython', 'program', 'quit']

Importer le module en dehors de la chaîne de code

Dans les exemples précédents, nous importions datetime dans le cadre du ‘program ' code objet, dans la chaîne. D'après notre compréhension de la façon dont exec() fonctionne, en exécutant le code ligne par ligne, il semblerait logique que le exec() fonction importerait le module au fur et à mesure qu'il fonctionnerait dynamiquement dans le code.

Mais que se passe-t-il si nous mettons l'instruction d'importation en dehors de notre 'program 'code? Dans l'exemple suivant, vous attendriez-vous à ce que le code fonctionne ?

import datetime
program = '''
print(datetime.datetime.now())
'''
exec(program)

Si vous avez répondu « oui », félicitations ! La sortie est en effet :

# 2021-01-25 12:28:32.629759

Si, comme moi quand j'ai vu cela pour la première fois, vous avez répondu «non», voyons ce qui s'est passé et comment cela a fonctionné. On voit bien le import datetime la commande est en dehors de la chaîne de code ‘program ' et nous avons demandé qu'il soit importé dans le exec() fonction.

Comme nous l'avons vu au début de ce blog le exec() la fonction a 3 paramètres ; object , locals et globals . Ainsi, même si l'importation datetime la déclaration n'est pas dans notre 'program ', il est automatiquement inclus dans le exec() fonction via le paramètre globals. Ce paramètre, globals() , autorise exec() l'accès à quoi que ce soit dans l'ensemble du programme, que ce soit notre intention ou non. Nous pouvons le confirmer en affichant le exec() dictionnaire :

exec(print(dir()))
# ['In', 'Out', …. 'datetime', 'exit', 'get_ipython', 'program', 'quit']

Comme dans l'exemple précédent d'importation dans la chaîne de code car le datetime module fait maintenant partie du exec() répertoire, il peut être appelé à nouveau, sans avoir à l'importer spécifiquement :

prog = '''
print(datetime.datetime.now())
'''
exec(prog)
# 2021-01-25 12:31:44.413763

Importation et paramètre global

Maintenant, même s'il peut y avoir des moments où vous avez exec() importer des bibliothèques et des variables à utiliser par défaut peut être utile, cela soulève des problèmes de sécurité importants. S'il n'est pas surveillé et entre de mauvaises mains, cela pourrait involontairement fournir un "accès détourné" aux scripts, fichiers et informations sur votre ordinateur.

Comme mentionné, exec() a trois paramètres, object , locals et globals et nous pouvons les utiliser pour contrôler la façon dont nous importons des bibliothèques. Comme les paramètres locaux et globaux sont facultatifs, si nous ne spécifions pas de paramètre local, le paramètre globals sera utilisé à la fois comme global et local.

Paramètre global et importation dans la chaîne de code

Examinons d'abord notre exemple d'importation à l'intérieur de la chaîne de code. Nous pouvons empêcher la bibliothèque de faire partie du exec() répertoire en plaçant un dictionnaire vide dans les globals {} paramètre.

program='''
import datetime
print(datetime.datetime.now())
'''
exec(program, {})
2021-01-25 12:34:09.591599

Cette fois, alors que notre code a fonctionné comme prévu, si nous imprimons le exec() répertoire, nous pouvons voir que le datetime a été ajouté par défaut aux éléments intégrés :

exec(print(dir()))
# ['In', 'Out', , '_oh', 'exit', 'get_ipython', 'program', 'quit']

Donc, si nous essayons maintenant d'utiliser le datetime module dans un autre exec() appel, nous obtenons le message d'erreur suivant :

prog = '''
print(datetime.datetime.now())
'''
exec(prog)
NameError: name 'datetime' is not defined

Paramètre global et importation en dehors de la chaîne de code

Nous pouvons restreindre exec() d'accéder à nos déclarations d'importation globales en plaçant un dictionnaire vide {} dans le globals paramètre. Cela empêchera les bibliothèques externes d'être importées en dehors de notre 'program ' chaîne de code. Dans l'exemple ci-dessous, nous allons importer deux des modules de la bibliothèque standard Python pour mieux illustrer notre propos :

import datetime
import random
program = '''
print(datetime.datetime.now())
print(random.randint(0,9))
'''
exec(program, {})
# NameError: name ‘datetime’ is not defined.

Maintenant, si nous imprimons le exec() répertoire, nous pouvons voir que les deux modules ont été importés, mais parce que nous avons spécifié que le globals() paramètre est vide, ils ne sont pas accessibles.

exec(print(dir()))

La sortie :

['In', 'Out…. '_oh', 'datetime', 'exit', 'get_ipython', 'program', 'quit', 'random']

Alternativement, nous pouvons stipuler les bibliothèques auxquelles nous voulons autoriser l'accès en les spécifiant dans le globals paramètre, par exemple :

import datetime
import random
program = '''
print(datetime.datetime.now())
print(random.randint(0,9))
'''
exec(program, {“datetime”:datetime})
2021-01-25 12:39:49.800751
NameError: name 'random' is not defined

Dans le code ci-dessus, seul le datetime module est inclus dans le globals paramètre, donc accessible, tandis que random reste restreint.

Paramètre local et importation en dehors de la chaîne de code

Tout comme le globals le paramètre offre un certain contrôle avec l'importation à l'aide de exec() il en va de même pour le paramètre locals. Avec locals() nous pouvons spécifier ce qui peut ou ne peut pas être inclus. Par exemple :

import datetime
program = '''
print(datetime.datetime.now())
'''
exec(program, {"__builtins__" : None}, {"datetime":datetime})
TypeError: 'NoneType' object is not subscriptable

Parce que nous avons bloqué l'accès à l'un des exec() intégrés avec le paramètre local {"__builtins__" : None} , ils ne sont pas accessibles. Ceci malgré le fait que nous ayons spécifié que le module est accessible par le paramètre global {“datetime”:datetime} .

Inversement on peut accorder un accès local même si on ne veut pas restreindre le paramètre global :

import datetime
program = '''
print(datetime.datetime.now())
'''
exec(program,{"datetime":datetime}, {})

Fournir un accès global à un paramètre local

Enfin, regardons ce qui se passe si nous avons un module disponible localement que nous souhaitons intégrer dans le global paramètre, par exemple :

program = '''
import datetime
def f():
    print(datetime.datetime.now())
 
'''
def test():
exec(program)
f()
test()
# NameError

Dans cet exemple, nous obtenons un NameError , car exec() est dans une fonction donc le datetime le module n'est disponible que dans cette fonction ‘test’ . Nous vérifions les modules disponibles dans le exec() répertoire ::

exec(print(dir()))
['In', 'Out', , '_oh', 'exit', 'get_ipython', 'program', 'quit']

Comme on peut le voir le datetime le module n'a pas été défini par défaut dans les éléments intégrés car il n'a été importé qu'en tant que local variable. Cela signifie que la bibliothèque n'est disponible que dans le test fonction et nous ne pouvons pas l'utiliser en dehors de cela. Nous pouvons vérifier cela en imprimant le répertoire locals de test dans la fonction :

program= '''
import datetime
def f():
    print(datetime.datetime.now())
 
'''
def test():
exec(program)
exec(print(locals()))    
f()
test()
 
{'datetime': <module 'datetime' from 'C:\\Users\\Rikesh\\anaconda3\\lib\\datetime.py'>, 'f': <function f at 0x000001C15E46D318>}
TypeError

Alors maintenant, plutôt que de restreindre l'accès, nous voulons en fait exec() pour importer datetime comme un paramètre global plutôt que local. Nous pouvons le faire avec la syntaxe suivante :

exec(program, globals())

Maintenant, si nous exécutons à nouveau notre code avec la syntaxe mise à jour :

program= ‘’’
import datetime
def f():
    print(datetime.datetime.now())
 
'''
def test():
exec(program, globals())
f()
test()
# 2021-01-25 12:55:11.031992

Et juste pour confirmer que nous avons bien importé la bibliothèque correctement, imprimons le exec() répertoire :

exec(print(dir()))
['In', 'Out', …. 'datetime', 'exit', 'get_ipython', 'program', 'quit']

Résumé

Nous avons vu quelques options pour importer des bibliothèques dans le exec() fonction. L'essentiel à garder à l'esprit est que oui, cela peut être fait, mais la prudence est toutefois recommandée !

Lors de l'utilisation de exec() , en particulier avec l'importation, la portée qu'il offre pour exécuter et exécuter dynamiquement du code peut être un outil très puissant s'il est utilisé correctement. Utilisé sans précaution, il peut causer de graves problèmes car vous pourriez accorder un accès détourné à votre ordinateur. Cependant, l'utilisation correcte des paramètres globaux et locaux vous offre un certain contrôle et doit donc toujours être incorporée dans votre code - pour éviter toute conséquence imprévue.