web-dev-qa-db-fra.com

Passer la variable à l'importation

Supposons que vous ayez du travail fastidieux à faire lorsqu'un module/classe est importé pour la première fois. Cette fonctionnalité dépend d'une variable passée. Cela ne doit être fait que lorsque le module/la classe est chargé. Toutes les instances de la classe peuvent ensuite utiliser le résultat.

Par exemple, j'utilise rpy2:

import rpy2.robjects as robjects

PATH_TO_R_SOURCE = ## I need to pass this
robjects.r.source(PATH_TO_R_SOURCE, chdir = True) ## this takes time

class SomeClass:
  def __init__(self, aCurve):
    self._curve = aCurve

  def processCurve(self):
    robjects.r['someRFunc'](robjects.FloatVector(self._curve))

Suis-je obligé de créer une fonction de niveau module que j'appelle pour effectuer le travail?

import someClass
someClass.sourceRStuff(PATH_TO_R_SOURCE)
x = someClass.SomeClass([1,2,3,4])
etc...
28
Mark

Avoir une fonction de module init n'est pas inconnu. Pygame le fait pour les fonctions d’initialisation sdl. Alors oui, votre meilleur pari est probablement

import someModule
someModule.init(NECESSARY_DATA)
x = someModule.someClass(range(1, 5))
24
nmichaels

Je devais faire quelque chose de similaire pour mon projet. Si vous ne voulez pas vous fier au script d'appel pour exécuter la fonction d'initialisation, vous pouvez ajouter votre propre fonction intégrée Python, qui est ensuite disponible pour tous les modules au moment de l'exécution.

Veillez à nommer votre fonction intégrée en quelque chose d'unique qui risque peu d'entraîner une collision d'espace de noms (par exemple, myapp_myvarname).

run.py

import __builtin__
__builtin__.myapp_PATH_TO_R_SOURCE = 'path.to.r.source'
import someClass

module oneClass .py

import rpy2.robjects as robjects
import __builtin__

if hasattr(__builtin__, "myapp_PATH_TO_R_SOURCE"):
    PATH_TO_R_SOURCE = __builtin__.myapp_PATH_TO_R_SOURCE
else:
    PATH_TO_R_SOURCE = ## Some default value or Null for Exception handling
robjects.r.source(PATH_TO_R_SOURCE, chdir = True)

...

Cela fonctionne bien pour les variables qui peuvent avoir une valeur par défaut mais que vous souhaitez autoriser le remplacement lors de l'importation. Si la variable __builtin__ n'est pas définie, il utilisera une valeur par défaut.

Edit: Certains considèrent cela comme un exemple de "correction de singe". Pour une solution plus élégante sans patch de singe, voir mon autre réponse .

12
Aaron D

S'il n'y a qu'un seul élément de configuration à définir, j'ai trouvé qu'il était correct de remplacer le __builtin__ de python, mais il s'agit d'un exemple de "Correction de singe" qui est mal vu par certains.

Une façon plus simple de le faire, ce qui est très utile pour plusieurs éléments de configuration de votre projet consiste à créer un module de configuration distinct qui est importé en premier par votre code d'habillage, puis les éléments définis à l'exécution, avant que votre module fonctionnel ne l'importe. Ce modèle est souvent utilisé dans d'autres projets.

myconfig/__ init__.py:

PATH_TO_R_SOURCE   = '/default/R/source/path'
OTHER_CONFIG_ITEM  = 'DEFAULT'
PI                 = 3.14

monmodule/__ init__.py:

import myconfig

PATH_TO_R_SOURCE = myconfig.PATH_TO_R_SOURCE
robjects.r.source(PATH_TO_R_SOURCE, chdir = True) ## this takes time

class SomeClass:
  def __init__(self, aCurve):
    self._curve = aCurve

if myconfig.VERSION is not None:
  version = myconfig.VERSION
else:
  version = "UNDEFINED"

two_pi = myconfig.PI * 2

Et vous pouvez modifier le comportement de votre module au moment de l'exécution à partir du wrapper:

run.py:

import myconfig

myconfig.PATH_TO_R_SOURCE = 'actual/path/to/R/source'
myconfig.PI = 3.14159
# we can even add a new configuration item that isn't present in the original myconfig:
myconfig.VERSION="1.0"

import mymodule
print "Mymodule.two_pi = %r" % mymodule.two_pi
print "Mymodule.version is %s" % mymodule.version

Sortie:

> Mymodule.two_pi = 6.28318
> Mymodule.version is 1.0
6
Aaron D

Il n'y a aucun moyen de passer une variable à l'importation.

Quelques idées:

  • obliger le module à obtenir la variable du module appelant en utilisant l'inspection; pas très pythonique
  • utiliser une fonction Init pour le module, c'est le meilleur moyen
4
leoluk

Quelques autres options pouvant atteindre votre objectif (bien qu'une fonction init () soit probablement plus propre):

  • Utiliser une variable d'environnement
  • Utilisez un module séparé M pour contenir cette variable, que l'importateur définirait. Ensuite, le module importé pourrait savoir où trouver M ou s’appuyer sur sys.meta_path pour l’obtenir.
3
tiho

Non, vous n'êtes pas coincé avec une fonction de niveau module, c'est probablement la meilleure option. Vous pouvez également utiliser les décorateurs intégrés staticmethod ou classmethod pour en faire une méthode sur someSclass pouvant être appelée avant son instanciation.

Cela aurait du sens seulement si tout ce qui n’était pas someClass était utilisable sans initialisation et je pense toujours qu’une fonction au niveau du module est préférable.

2
aaronasterling

Il y a deux solutions auxquelles je peux penser, qui sont toutes les deux des solutions très simples. La première consiste à abandonner les importations et à exécuter votre script comme ceci

sys.argv[1] = "argument 1"
sys.argv[2] = "argument 2"
execfile("/path/to/dependency.py")  #depreciated in python 3.x

La seconde consiste à placer vos arguments dans un fichier temporaire externe, puis à lire ce fichier dans la dépendance.

0
SarcasticSully

Pourriez-vous bénéficier d'un proxy qui implémente le chargement différé? 

Consultez l'état actif " Recette du module importé ".

0
Escualo