web-dev-qa-db-fra.com

Comment utiliser correctement la fonction d'importation de python __import __ ()

J'essaie de répliquer from foo.bar import object En utilisant la fonction __import__ Et il me semble avoir heurté un mur.

from glob import glob Est simple: glob = __import__("glob",glob) ou glob = __import__("glob").glob

Le problème que j'ai, c'est que j'importe à partir d'un package (c'est-à-dire une barre) et que je veux que le script du package soit la source de l'importation.

Donc ce que j'aimerais, c'est quelque chose comme

string_to_import = "bar"
object = __import__("foo",string_to_import).object

Mais cela importe juste le __init__ Dans le paquet foo.

Comment cela peut-il être fait?

EDIT: Lorsque j'utilise l'évidence, seul le __init__ Est appelé

__import__("foo.bar")
<module 'foo' from 'foo/__init__.pyc'>
39
jdborg

Le __import__ la fonction renverra le module de niveau supérieur d'un package, sauf si vous passez un argument non vide fromlist:

_temp = __import__('foo.bar', fromlist=['object']) 
object = _temp.object

Voir les documents Python sur les __import__ fonction .

38
Eric H.

Comment utiliser correctement la fonction __import__() de python?

Il existe deux types d'utilisations:

  • importation directe
  • un crochet pour modifier le comportement d'importation

Pour la plupart, vous n'avez pas vraiment besoin de faire non plus.

Pour l'importation dans l'espace utilisateur

La meilleure pratique consiste à utiliser importlib à la place. Mais si vous insistez:

Utilisation triviale:

>>> sys = __import__('sys')
>>> sys
<module 'sys' (built-in)>

Compliqué:

>>> os = __import__('os.path')
>>> os
<module 'os' from '/home/myuser/anaconda3/lib/python3.6/os.py'>
>>> os.path
<module 'posixpath' from '/home/myuser/anaconda3/lib/python3.6/posixpath.py'>

Si vous voulez le module enfant le plus à droite dans le nom, passez une liste non vide, par ex. [None], à fromlist:

>>> path = __import__('os.path', fromlist=[None])
>>> path
<module 'posixpath' from '/home/myuser/anaconda3/lib/python3.6/posixpath.py'>

Ou, comme le déclare la documentation, utilisez importlib.import_module:

>>> importlib = __import__('importlib')
>>> futures = importlib.import_module('concurrent.futures')
>>> futures
<module 'concurrent.futures' from '/home/myuser/anaconda3/lib/python3.6/concurrent/futures/__init__.py'>

Documentation

Les documents pour __import__ sont les fonctions internes les plus déroutantes.

__import__(...)
    __import__(name, globals=None, locals=None, fromlist=(), level=0) -> module

    Import a module. Because this function is meant for use by the Python
    interpreter and not for general use it is better to use
    importlib.import_module() to programmatically import a module.

    The globals argument is only used to determine the context;
    they are not modified.  The locals argument is unused.  The fromlist
    should be a list of names to emulate ``from name import ...'', or an
    empty list to emulate ``import name''.
    When importing a module from a package, note that __import__('A.B', ...)
    returns package A when fromlist is empty, but its submodule B when
    fromlist is not empty.  Level is used to determine whether to perform 
    absolute or relative imports. 0 is absolute while a positive number
    is the number of parent directories to search relative to the current module.

Si vous le lisez attentivement, vous avez le sentiment que l'API était à l'origine destinée à permettre le chargement paresseux des fonctions à partir des modules. Cependant, ce n'est pas ainsi que CPython fonctionne, et je ne sais pas si d'autres implémentations de Python ont réussi à le faire.

Au lieu de cela, CPython exécute tout le code dans l'espace de noms du module lors de sa première importation, après quoi le module est mis en cache dans sys.modules.

__import__ peut toujours être utile. Mais comprendre ce qu'il fait sur la base de la documentation est plutôt difficile.

Utilisation complète de __import__

Pour adapter toutes les fonctionnalités afin de montrer la __import__ API, voici une fonction wrapper avec une API plus propre et mieux documentée.

def importer(name, root_package=False, relative_globals=None, level=0):
    """ We only import modules, functions can be looked up on the module.
    Usage: 

    from foo.bar import baz
    >>> baz = importer('foo.bar.baz')

    import foo.bar.baz
    >>> foo = importer('foo.bar.baz', root_package=True)
    >>> foo.bar.baz

    from .. import baz (level = number of dots)
    >>> baz = importer('baz', relative_globals=globals(), level=2)
    """
    return __import__(name, locals=None, # locals has no use
                      globals=relative_globals, 
                      fromlist=[] if root_package else [None],
                      level=level)

Pour démontrer, par exemple d'un package soeur à baz:

baz = importer('foo.bar.baz')    
foo = importer('foo.bar.baz', root_package=True)
baz2 = importer('bar.baz', relative_globals=globals(), level=2)

assert foo.bar.baz is baz is baz2

Accès dynamique aux noms dans le module

Pour accéder dynamiquement aux globales par nom depuis le module baz, utilisez getattr. Par exemple:

for name in dir(baz):
    print(getattr(baz, name))

Crochet pour modifier le comportement d'importation

Vous pouvez utiliser __import__ pour modifier ou intercepter le comportement d'importation. Dans ce cas, imprimons simplement les arguments qu'il permet de démontrer que nous l'interceptons:

old_import = __import__

def noisy_importer(name, locals, globals, fromlist, level):
    print(f'name: {name!r}')
    print(f'fromlist: {fromlist}')
    print(f'level: {level}')
    return old_import(name, locals, globals, fromlist, level)

import builtins
builtins.__import__ = noisy_importer

Et maintenant, lorsque vous importez, vous pouvez voir ces arguments importants.

>>> from os.path import join as opj
name: 'os.path'
fromlist: ('join',)
level: 0
>>> opj
<function join at 0x7fd08d882618>

Peut-être que dans ce contexte, obtenir les globaux ou les locaux pourrait être utile, mais aucune utilisation spécifique de cela ne vient immédiatement à l'esprit.

37
Aaron Hall

Vous devez utiliser importlib.import_module, __import__ N'est pas conseillé en dehors de l'interpréteur.

Dans la docstring de __import__:

Importez un module. Parce que cette fonction est destinée à être utilisée par l'interpréteur Python et non à usage général, il est préférable d'utiliser importlib.import_module () pour importer par programme un module.

Il prend également en charge importations relatives .

22
Zulu

Plutôt que d'utiliser le __import__ fonction J'utiliserais la fonction getattr:

model = getattr(module, model_s)

où module est le module dans lequel rechercher et et model_s est votre chaîne de modèle. Le __import__ La fonction n'est pas destinée à être utilisée de manière lâche, alors que cette fonction vous obtiendra ce que vous voulez.

3
Eric H.