Wie rufe ich setattr () für das aktuelle Modul auf?

140

Was übergebe ich als ersten Parameter " object" an die Funktion setattr(object, name, value), um Variablen auf dem aktuellen Modul zu setzen?

Beispielsweise:

setattr(object, "SOME_CONSTANT", 42);

den gleichen Effekt geben wie:

SOME_CONSTANT = 42

innerhalb des Moduls, das diese Zeilen enthält (mit der richtigen object).

Ich generiere dynamisch mehrere Werte auf Modulebene, und da ich __getattr__auf Modulebene nicht definieren kann , ist dies mein Fallback.

Matt Joiner
quelle

Antworten:

220
import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

oder ohne Verwendung setattr(was den Buchstaben der Frage bricht, aber die gleichen praktischen Zwecke erfüllt ;-):

globals()[name] = value

Hinweis : Letzteres entspricht im Modulumfang:

vars()[name] = value

Das ist etwas prägnanter, funktioniert aber nicht innerhalb einer Funktion ( vars()gibt die Variablen des Bereichs an, in dem es aufgerufen wird: die Variablen des Moduls, wenn es im globalen Bereich aufgerufen wird, und dann ist es in Ordnung, es R / W zu verwenden, aber die Funktion Variablen, wenn sie in einer Funktion aufgerufen werden, und dann als R / O behandelt werden müssen - die Python-Online-Dokumente können in Bezug auf diese spezielle Unterscheidung etwas verwirrend sein).

Alex Martelli
quelle
9
Die Dokumente geben eine Warnung zum Ändern von vars () aus. docs.python.org/library/functions.html#vars . Wann ist das in Ordnung?
Unutbu
2
@ ~ unutbu, ich würde nicht wirklich sagen, dass es ganz "okay" ist, aber es wird funktionieren, wenn Sie vars()auf Modulebene und nicht innerhalb einer Funktion aufrufen .
Mike Graham
4
vars()ist äquivalent zu globals()at module scope (und gibt so ein wahres, modifizierbares Diktat zurück), aber locals()at function scope (und gibt so ein niemals zu modifizierendes Pseudodict zurück). Ich benutze vars()im Modulbereich, da es 3 Zeichen, eine Silbe, gegenüber seinem Synonym in diesem Bereich speichert globals();-)
Alex Martelli
14
Ja, es hätte die wichtigste Optimierung des Python-Compilers zerstört: Die lokalen Variablen einer Funktion werden nicht in einem Diktat gespeichert, sie befinden sich in einem engen Wertevektor, und jeder Zugriff auf lokale Variablen verwendet den Index in diesem Vektor, nicht eine Namenssuche. Um die Optimierung zu umgehen und das gewünschte Diktat zu erzwingen, starten Sie die Funktion mit exec '': Zeit einer Funktion mit ein paar wesentlichen Schleifen in jede Richtung, und Sie werden die Bedeutung dieser Kernoptimierung für die Leistung von Python erkennen.
Alex Martelli
3
@msw, ich denke du hast vergessen "Praktikabilität schlägt Reinheit" ;-).
Alex Martelli
6

Was ist falsch daran, wenn Sie Variablen mit Modulbereich innerhalb des Moduls festlegen müssen global?

# my_module.py

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

so:

>>> 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
quelle
1
Ja, ich habe diese global but not reallyErklärung immer (wobei "immer" als "die letzten Monate, in denen ich Python gelernt habe" definiert ist) als rätselhaft empfunden. Ich nehme an, es könnte ein historisches Relikt sein, das vor Modul-Namespaces liegt.
Msw
1
Die ursprüngliche Frage lautet, wie ein Attribut festgelegt werden soll, dessen Name durch eine Zeichenfolge angegeben wird (dasselbe, wonach ich gerade gesucht habe). Dies würde also nicht helfen.
Curt
6

In Python 3.7 können Sie __getattr__auf Modulebene verwenden ( zugehörige Antwort ).

Gemäß PEP 562 :

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")
Trey Hunner
quelle
-1
  1. Das würdest du nicht. Du würdestglobals()["SOME_CONSTANT"] = 42
  2. Das würdest du nicht. Sie würden dynamisch generierten Inhalt an einem anderen Ort als einem Modul speichern.
Mike Graham
quelle
Ja, SOME_CONSTANTzur Laufzeit berechnet ist nicht genau konstant. Und wenn globals()Ihnen dies nicht zur Verfügung steht, müssen Sie in ein anderes Modul greifen, um dessen Attribute zu ändern. Das wird die Leute sicherlich zum Staunen bringen.
Msw
3
Konstante und veränderbare schließen sich gegenseitig aus. Konstant und dynamisch generiert sind nicht. Die Werte, die ich generiere, sind immer die gleichen und werden basierend auf weiteren "Konstanten" bestimmt, um meinerseits Arithmetik und Eingabe zu sparen.
Matt Joiner