Comment appeler setattr () sur le module actuel?

140

Que dois-je passer comme premier paramètre " object" à la fonction setattr(object, name, value), pour définir des variables sur le module actuel?

Par exemple:

setattr(object, "SOME_CONSTANT", 42);

donnant le même effet que:

SOME_CONSTANT = 42

dans le module contenant ces lignes (avec le bon object).

Je génère dynamiquement plusieurs valeurs au niveau du module, et comme je ne peux pas définir __getattr__au niveau du module, c'est ma solution de secours.

Matt Joiner
la source

Réponses:

220
import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

ou, sans utiliser setattr(qui casse la lettre de la question mais répond aux mêmes fins pratiques ;-):

globals()[name] = value

Remarque : au niveau du module, ce dernier équivaut à:

vars()[name] = value

ce qui est un peu plus concis, mais ne fonctionne pas à partir d'une fonction ( vars()donne les variables de la portée à laquelle elle est appelée: les variables du module lorsqu'il est appelé à portée globale, puis il est OK de l'utiliser R / W, mais la fonction variables lorsqu'il est appelé dans une fonction, puis il doit être traité comme R / O - la documentation en ligne Python peut être un peu déroutante sur cette distinction spécifique).

Alex Martelli
la source
9
La documentation donne un avertissement sur la modification de vars (). docs.python.org/library/functions.html#vars . Quand est-il possible de faire cela?
unutbu
2
@ ~ unutbu, je ne dirais pas vraiment que c'est tout à fait "ok", mais cela fonctionnera lorsque vous appelez vars()au niveau du module de portée plutôt qu'à l'intérieur d'une fonction.
Mike Graham
4
vars()équivaut à globals()at module scope (et renvoie donc un dict vrai et modifiable) mais à locals()at function scope (et donc retourne un pseudodict à ne jamais être modifié). J'utilise vars()à la portée du module car il enregistre 3 caractères, une syllabe, par rapport à son synonyme dans cette portée globals();-)
Alex Martelli
14
Oui, cela aurait détruit l'optimisation la plus importante du compilateur Python: les variables locales d'une fonction ne sont pas conservées dans un dict, elles sont dans un vecteur de valeurs serré, et chaque accès aux variables locales utilise l'index dans ce vecteur, pas une recherche de nom. Pour vaincre l'optimisation, forçant le dict que vous désirez à exister, démarrez la fonction avec exec '': chronométrez une fonction avec quelques boucles substantielles dans chaque sens, et vous verrez l'importance de cette optimisation de base pour les performances de Python.
Alex Martelli
3
@msw, je pense que vous avez oublié "la fonctionnalité bat la pureté" ;-).
Alex Martelli
6

Si vous devez définir des variables de portée du module à partir du module, quel est le problème global?

# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

Donc:

>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']
msw
la source
1
Ouais, j'ai toujours (où "toujours" est défini comme "les derniers mois que j'ai appris Python") trouvé cette global but not reallydéclaration déroutante. Je suppose que ce peut être une relique historique qui précède les espaces de noms de module.
msw
1
La question originale est de savoir comment définir un attribut dont le nom est donné par une chaîne (la même chose que je recherchais actuellement), donc cela n'aiderait pas.
Curt
6

En Python 3.7, vous pourrez utiliser __getattr__au niveau du module ( réponse associée ).

Selon PEP 562 :

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")
Trey Hunner
la source
-1
  1. Vous ne le feriez pas. Vous feriezglobals()["SOME_CONSTANT"] = 42
  2. Vous ne le feriez pas. Vous stockeriez le contenu généré dynamiquement ailleurs que dans un module.
Mike Graham
la source
Oui, le SOME_CONSTANTcalcul au moment de l'exécution n'est pas exactement constant. Et si globals()vous n'êtes pas disponible, vous devez accéder à un autre module pour modifier ses attributs; cela va forcément amener les gens à s'interroger.
msw
3
Constante et mutable s'excluent mutuellement. Constante et générée dynamiquement ne le sont pas. Les valeurs que je génère sont toujours les mêmes, et déterminées en fonction d'autres «constantes», pour économiser sur l'arithmétique et la saisie de ma part.
Matt Joiner