Wie mache ich eine modulübergreifende Variable?

120

Die __debug__Variable ist teilweise praktisch, weil sie jedes Modul betrifft. Wie würde ich es tun, wenn ich eine andere Variable erstellen möchte, die auf die gleiche Weise funktioniert?

Die Variable (seien wir originell und nennen sie 'foo') muss nicht wirklich global sein, in dem Sinne, dass wenn ich foo in einem Modul ändere, sie in anderen Modulen aktualisiert wird. Es wäre in Ordnung, wenn ich vor dem Importieren anderer Module foo setzen könnte und sie dann den gleichen Wert dafür sehen würden.

Dan Homerick
quelle

Antworten:

113

Ich unterstütze diese Lösung in keiner Weise, Form oder Gestalt. Wenn Sie dem __builtin__Modul jedoch eine Variable hinzufügen , kann auf diese wie auf eine globale Variable aus einem anderen Modul zugegriffen werden, das __builtin__standardmäßig alle enthält.

a.py enthält

print foo

b.py enthält

import __builtin__
__builtin__.foo = 1
import a

Das Ergebnis ist, dass "1" gedruckt wird.

Bearbeiten: Das __builtin__Modul ist als lokales Symbol verfügbar __builtins__- das ist der Grund für die Diskrepanz zwischen zwei dieser Antworten. Beachten Sie auch, dass __builtin__dies builtinsin python3 umbenannt wurde.

Curt Hagenlocher
quelle
2
Gibt es einen Grund, warum Ihnen diese Situation nicht gefällt?
Software Enthusiastic
31
Zum einen bricht es die Erwartungen der Leute, wenn sie Code lesen. "Was ist dieses 'foo'-Symbol, das hier verwendet wird? Warum kann ich nicht sehen, wo es definiert ist?"
Curt Hagenlocher
9
Es ist auch anfällig für Chaos, wenn eine zukünftige Version von Python den Namen verwendet, den Sie als tatsächlich eingebaut gewählt haben.
Intuitiert
4
Dies ist eine gute Lösung für Dinge wie das Teilen einer Datenbankverbindung mit importierten Modulen. Zur Überprüfung der Integrität stelle ich sicher, dass das importierte Modul bestätigt wird hasattr(__builtin__, "foo").
Mike Ellis
4
Für alle, die diese Antwort lesen: NICHT! MACHEN ! DIES ! Wirklich nicht.
Bruno Desthuilliers
160

Wenn Sie eine globale modulübergreifende Variable benötigen, reicht möglicherweise nur eine einfache globale Variable auf Modulebene aus.

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

Prüfung:

$ python b.py
# -> 1 2

Beispiel aus der Praxis : Djangos global_settings.py (obwohl in Django-Apps Einstellungen durch Importieren des Objekts verwendet werden django.conf.settings ).

jfs
quelle
3
Besser, weil es mögliche Namespace-Konflikte vermeidet
bgw
Was ist, wenn das zu importierende Modul in diesem Fall a.pyenthält main()? Ist das wichtig?
Sedeh
4
@sedeh: nein. Wenn a.py auch als Skript ausgeführt wird, verwenden Sie if __name__=="__main__"Guard darin, um zu vermeiden, dass beim Import unerwarteter Code ausgeführt wird.
JFS
6
In der realen Welt muss man mit dieser Lösung etwas vorsichtig sein. Wenn ein Programmierer Ihre 'globale' Variable mit 'von einer Importvariablen' aufnimmt (versuchen Sie diese Variante in c.py), erhält er zum Zeitpunkt des Imports eine Kopie der Variablen.
Paul Whipp
1
@ PaulWhipp: falsch (Hinweis: verwenden id(), um die Identität zu überprüfen)
jfs
24

Definieren Sie ein Modul (nennen Sie es "globalbaz") und definieren Sie die darin enthaltenen Variablen. Alle Module, die dieses "Pseudoglobal" verwenden, sollten das "globalbaz" -Modul importieren und mit "globalbaz.var_name" darauf verweisen.

Dies funktioniert unabhängig vom Ort der Änderung. Sie können die Variable vor oder nach dem Import ändern. Das importierte Modul verwendet den neuesten Wert. (Ich habe dies in einem Spielzeugbeispiel getestet)

Zur Verdeutlichung sieht globalbaz.py folgendermaßen aus:

var_name = "my_useful_string"
Hayalci
quelle
24

Ich glaube, dass es viele Umstände gibt, unter denen es sinnvoll ist, und es vereinfacht die Programmierung, einige globale Elemente zu haben, die über mehrere (eng gekoppelte) Module hinweg bekannt sind. In diesem Sinne möchte ich ein wenig auf die Idee eingehen, ein Modul von Globals zu haben, das von den Modulen importiert wird, die auf sie verweisen müssen.

Wenn es nur ein solches Modul gibt, nenne ich es "g". Darin weise ich jeder Variablen, die ich als global behandeln möchte, Standardwerte zu. In jedem Modul, das eines von ihnen verwendet, verwende ich nicht "from g import var", da dies nur zu einer lokalen Variablen führt, die nur zum Zeitpunkt des Imports von g initialisiert wird. Ich mache die meisten Referenzen in der Form g.var und dem "g". dient als ständige Erinnerung daran, dass es sich um eine Variable handelt, auf die möglicherweise andere Module zugreifen können.

Wenn der Wert einer solchen globalen Variablen in einer Funktion eines Moduls häufig verwendet werden soll, kann diese Funktion eine lokale Kopie erstellen: var = g.var. Es ist jedoch wichtig zu wissen, dass Zuweisungen an var lokal sind und die globale g.var nicht aktualisiert werden kann, ohne g.var in einer Zuweisung explizit zu referenzieren.

Beachten Sie, dass Sie auch mehrere solcher globalen Module haben können, die von verschiedenen Teilmengen Ihrer Module gemeinsam genutzt werden, um die Kontrolle zu behalten. Der Grund, warum ich Kurznamen für meine globalen Module verwende, besteht darin, zu vermeiden, dass der Code zu stark mit Vorkommen überladen wird. Mit nur wenig Erfahrung werden sie mit nur 1 oder 2 Zeichen mnemonisch genug.

Es ist immer noch möglich, eine Zuordnung beispielsweise zu gx vorzunehmen, wenn x nicht bereits in g definiert wurde, und ein anderes Modul kann dann auf gx zugreifen. Obwohl der Interpreter dies zulässt, ist dieser Ansatz nicht so transparent, und ich vermeide es es. Es besteht weiterhin die Möglichkeit, versehentlich eine neue Variable in g aufgrund eines Tippfehlers im Variablennamen für eine Zuweisung zu erstellen. Manchmal ist eine Untersuchung von dir (g) nützlich, um Überraschungsnamen zu entdecken, die durch einen solchen Unfall entstanden sein könnten.

David Vanderschel
quelle
7
Diese interessante Beobachtung löste mein Problem: "Ich verwende nicht" from g import var ", da dies nur zu einer lokalen Variablen führt, die erst zum Zeitpunkt des Imports aus g initialisiert wird." Es scheint vernünftig anzunehmen, dass "from..import" dasselbe ist wie "import", aber dies ist nicht wahr.
Curtis Yallop
9

Sie können die Globals eines Moduls an ein anderes übergeben:

In Modul A:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

In Modul B:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3
user394430
quelle
7

Globale Variablen sind normalerweise eine schlechte Idee, aber Sie können dies tun, indem Sie Folgendes zuweisen __builtins__:

__builtins__.foo = 'something'
print foo

Außerdem sind Module selbst Variablen, auf die Sie von jedem Modul aus zugreifen können. Wenn Sie also ein Modul mit dem Namen definieren my_globals.py:

# my_globals.py
foo = 'something'

Dann können Sie das auch von überall aus verwenden:

import my_globals
print my_globals.foo

Die Verwendung von Modulen anstelle von Änderungen __builtins__ist im Allgemeinen eine sauberere Methode, um solche Globals zu erstellen.

spiv
quelle
3
__builtins__ist eine CPython-Besonderheit, Sie sollten sie wirklich nicht verwenden - besser verwenden __builtin__(oder builtinsin Python3), wie die akzeptierte Antwort zeigt
Tobias Kienzler
5

Sie können dies bereits mit Variablen auf Modulebene tun. Module sind gleich, unabhängig davon, aus welchem ​​Modul sie importiert werden. Sie können die Variable also zu einer Variablen auf Modulebene in einem beliebigen Modul machen, in das sie sinnvoll eingefügt werden soll, und von anderen Modulen aus darauf zugreifen oder ihr zuweisen. Es ist besser, eine Funktion aufzurufen, um den Wert der Variablen festzulegen, oder sie zu einer Eigenschaft eines Singleton-Objekts zu machen. Auf diese Weise können Sie, wenn Sie beim Ändern der Variablen Code ausführen müssen, dies tun, ohne die externe Schnittstelle Ihres Moduls zu beschädigen.

Es ist normalerweise keine großartige Möglichkeit, Dinge zu tun - mit Globals selten -, aber ich denke, dies ist die sauberste Art, dies zu tun.

intuitiv
quelle
3

Ich wollte eine Antwort posten, dass es einen Fall gibt, in dem die Variable nicht gefunden wird.

Zyklische Importe können das Modulverhalten beeinträchtigen.

Beispielsweise:

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py.

import first

In diesem Beispiel sollte es offensichtlich sein, aber in einer großen Codebasis kann dies wirklich verwirrend sein.

Jonathan
quelle
1

Das klingt nach einer Änderung des __builtin__Namensraums. Es zu tun:

import __builtin__
__builtin__.foo = 'some-value'

Verwenden Sie das nicht __builtins__direkt (beachten Sie die zusätzlichen "s") - anscheinend kann dies ein Wörterbuch oder ein Modul sein. Dank an ΤΖΩΤΖΙΟΥ für diesen Hinweis finden Sie hier mehr .

Jetzt fooist überall verfügbar.

Ich empfehle dies im Allgemeinen nicht, aber die Verwendung liegt beim Programmierer.

Die Zuweisung muss wie oben erfolgen. Durch einfaches Einstellen foo = 'some-other-value'wird es nur im aktuellen Namespace festgelegt.

awatts
quelle
1
Ich erinnere mich (aus comp.lang.python), dass die Verwendung direkt eingebauter Elemente vermieden werden sollte. Importieren Sie stattdessen eingebautes Material und verwenden Sie es, wie Curt Hagenlocher vorgeschlagen hat.
Zot
1

Ich benutze dies für ein paar eingebaute primitive Funktionen, die mir wirklich fehlten. Ein Beispiel ist eine Suchfunktion, die dieselbe Verwendungssemantik wie Filter, Map, Reduce hat.

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

Sobald dies ausgeführt wird (z. B. durch Importieren in der Nähe Ihres Einstiegspunkts), können alle Ihre Module find () verwenden, als ob es offensichtlich eingebaut wäre.

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

Hinweis: Sie können dies natürlich mit einem Filter und einer anderen Zeile tun, um die Länge auf Null zu testen, oder mit einer Verringerung einer seltsamen Zeile, aber ich fand es immer seltsam.

Brian Arsuaga
quelle
1

Mit einem Wörterbuch konnte ich modulübergreifende modifizierbare (oder veränderbare ) Variablen erzielen :

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

Beim Start test_wait_app_up_failbeträgt die tatsächliche Zeitüberschreitungsdauer 3 Sekunden.

Foudfou
quelle
1

Ich habe mich gefragt, ob es möglich ist, einige der Nachteile der Verwendung globaler Variablen (siehe z. B. http://wiki.c2.com/?GlobalVariablesAreBad ) zu vermeiden, indem ein Klassennamensraum anstelle eines globalen / Modul-Namespace verwendet wird, um Werte von Variablen zu übergeben . Der folgende Code zeigt an, dass die beiden Methoden im Wesentlichen identisch sind. Es ist ein kleiner Vorteil, Klassennamensräume zu verwenden, wie unten erläutert.

Die folgenden Codefragmente zeigen auch, dass Attribute oder Variablen sowohl in globalen / Modul-Namespaces als auch in Klassennamensräumen dynamisch erstellt und gelöscht werden können.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

Ich nenne dieses Modul "Wand", da es zum Abprallen von Variablen verwendet wird. Es dient als Leerzeichen zum vorübergehenden Definieren globaler Variablen und klassenweiter Attribute der leeren Klasse 'Router'.

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

Dieses Modul importiert wall und definiert eine einzelne Funktion, sourcefndie eine Nachricht definiert und über zwei verschiedene Mechanismen ausgibt, einen über globale und einen über die Router-Funktion. Beachten Sie, dass die Variablen wall.msgund wall.router.messagehier zum ersten Mal in ihren jeweiligen Namespaces definiert werden.

dest.py.

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

Dieses Modul definiert eine Funktion, destfndie die zwei verschiedenen Mechanismen verwendet, um die von der Quelle ausgegebenen Nachrichten zu empfangen. Dies ermöglicht möglicherweise, dass die Variable 'msg' nicht vorhanden ist. destfnlöscht auch die Variablen, sobald sie angezeigt wurden.

main.py.

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

Dieses Modul ruft die zuvor definierten Funktionen nacheinander auf. Nach dem ersten Aufruf dest.destfnder Variablen wall.msgund wall.router.msgnicht mehr vorhanden.

Die Ausgabe des Programms lautet:

global: Hallo Welt!
Router: Hallo Welt!
global: keine Nachricht
Router: keine Nachricht

Die obigen Codefragmente zeigen, dass die Mechanismen module / global und class / class variable im Wesentlichen identisch sind.

Wenn viele Variablen gemeinsam genutzt werden sollen, kann die Namespace-Verschmutzung entweder mithilfe mehrerer Wandmodule, z. B. Wand1, Wand2 usw., oder durch Definieren mehrerer Routertypklassen in einer einzigen Datei verwaltet werden. Letzteres ist etwas aufgeräumter und stellt daher möglicherweise einen geringfügigen Vorteil für die Verwendung des Mechanismus mit Klassenvariablen dar.

robertofbaycot
quelle