2008-09-26 8 views
106

Die Variable __debug__ ist zum Teil handlich, weil sie jedes Modul betrifft. Wenn ich eine andere Variable erstellen möchte, die auf die gleiche Weise funktioniert, wie würde ich das tun?Python: Wie macht man eine Cross-Modul-Variable?

Die Variable (lasst uns Original sein und es 'foo' nennen) muss nicht wirklich global sein, in dem Sinne, dass wenn ich foo in einem Modul ändere, es in anderen aktualisiert wird. Es würde mir gut gehen, wenn ich foo vor dem Importieren anderer Module einstellen könnte und dann würden sie den gleichen Wert dafür sehen.

Antwort

94

Ich unterstütze diese Lösung in keiner Weise, Form oder Form. Wenn Sie jedoch eine Variable zum __builtin__-Modul hinzufügen, ist es wie bei einem globalen Modul von einem anderen Modul zugänglich, das __builtin__ enthält - standardmäßig alle davon.

a.py enthält

print foo 

b.py

import __builtin__ 
__builtin__.foo = 1 
import a 

enthält das Ergebnis ist, dass "1" gedruckt wird.

Edit: Das __builtin__ Modul als das lokale Symbol __builtins__ zur Verfügung steht - das ist der Grund für die Diskrepanz zwischen zwei dieser Antworten. Beachten Sie auch, dass __builtin__ in python3 in builtins umbenannt wurde.

+2

Irgendein Grund, dass Sie diese Situation nicht mögen? –

+26

Zum einen bricht es die Erwartungen der Menschen, wenn sie Code lesen. "Was ist das 'foo' Symbol hier verwendet? Warum kann ich nicht sehen, wo es definiert ist?" –

+8

Es kann auch Schaden anrichten, wenn eine zukünftige Version von Python den von Ihnen gewählten Namen als tatsächlichen eingebauten Namen verwendet. – intuited

2

Das klingt wie Ändern der __builtin__ Namensraum. Um es zu machen:

import __builtin__ 
__builtin__.foo = 'some-value' 

Sie nicht die __builtins__ direkt verwenden (beachten Sie die zusätzlichen „s“) - offensichtlich kann dies ein Wörterbuch oder ein Modul sein. Dank ΤΖΩΤΖΙΟΥ können Sie mehr herausfinden, here.

Jetzt ist foo überall verfügbar.

Ich empfehle nicht, dies in der Regel zu tun, aber die Verwendung von dies ist an den Programmierer.

Die Zuordnung muss wie oben beschrieben erfolgen, nur die Einstellung foo = 'some-other-value' wird nur im aktuellen Namespace festgelegt.

+1

Ich erinnere mich (aus comp.lang.python), dass die Verwendung von __builtins__ sollte vermieden werden; Stattdessen importieren Sie __builtin__ und verwenden Sie das, wie Curt Hagenlocher vorgeschlagen hat. – tzot

20

Definieren Sie ein Modul (nennen Sie es "globalbaz") und haben Sie die Variablen darin definiert. Alle Module, die dieses "pseudoglobal" verwenden, sollten das Modul "globalbaz" 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. (Getestet habe ich diese in einem Spielzeug-Beispiel)

Zur Klarstellung globalbaz.py wie folgt aussieht:

var_name = "my_useful_string" 
131

Wenn Sie eine globale modulübergreifende Variable brauchen vielleicht nur einfache globale Variable auf Modulebene genügt .

a.py:

var = 1 

b.py:

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

c.py:

import a 
a.var = 2 

Test:

$ python b.py 
# -> 1 2 

Praxisbeispiel: Django's global_settings.py (wenn auch in Django apps Einstellungen verwendet werden durch den Import der Objektdjango.conf.settings).

+2

Besser, weil mögliche Namespace-Konflikte vermieden werden – bgw

+0

Was passiert, wenn das Modul, das Sie importieren, in diesem Fall 'a.py',' main() 'enthält? Ist es wichtig? – sedeh

+4

@sedeh: nein. Wenn a.py auch als Skript ausgeführt wird, verwenden Sie 'if __name __ ==" __ main __ "', um zu vermeiden, dass beim Import unerwarteter Code ausgeführt wird. – jfs

7

Globale Variablen sind in der Regel eine schlechte Idee, aber man kann dies zu __builtins__ durch die Zuordnung tun:

__builtins__.foo = 'something' 
print foo 

Auch die Module selbst sind Variablen, die von jedem Modul zugreifen können. Wenn Sie also ein Modul definieren my_globals.py genannt:

# my_globals.py 
foo = 'something' 

Dann können Sie von jedem beliebigen Ort als auch verwenden, dass:

import my_globals 
print my_globals.foo 

Module verwenden, anstatt zu modifizieren __builtins__ ist im Allgemeinen ein sauberer Weg Globals dieser Art zu tun .

+2

'__builtins__' ist eine CPython-Eigenschaft, die Sie wirklich nicht verwenden sollten - verwenden Sie besser' __builtin__' (oder 'builtins' in Python3) als [die akzeptierte Antwort] (http://stackoverflow.com/a/142566/321973) zeigt –

9

Sie können die Globals eines Moduls passieren onother:

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 
+2

zum Testen an! –

5

Sie können dies bereits tun mit Modulebene Variablen. Module sind gleich, egal von welchem ​​Modul sie importiert werden. So können Sie die Variable zu einer Modul-Level-Variablen machen, in welchem ​​Modul es auch sinnvoll ist, sie einzufügen und von anderen Modulen aus darauf zuzugreifen oder ihr zuzuordnen. Es wäre 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 Code ausführen, wenn sich die Variable geändert hat, ohne die externe Schnittstelle des Moduls zu unterbrechen.

Es ist normalerweise keine gute Möglichkeit, Dinge zu tun - mit Globals ist selten - aber ich denke, das ist der sauberste Weg, es zu tun.

1

Ich benutze dies für ein paar eingebaute primitive Funktionen, die ich wirklich vermisst habe. Ein Beispiel ist eine Suchfunktion, die dieselbe Verwendungssemantik wie filter, map, reduce aufweist.

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 (zum Beispiel durch in der Nähe Ihrer Eintrittspunkt Import) alle Module können find() verwenden, als ob, natürlich, es in gebaut wurde

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

. Hinweis: Sie können dies natürlich mit Filter und einer anderen Zeile tun, um auf Null Länge zu testen, oder mit einer Art seltsamer Linie, aber ich fand es immer komisch.

20

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

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

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

Beachten Sie, dass Sie auch mehrere solcher Globals-Module haben können, die von verschiedenen Untergruppen Ihrer Module gemeinsam genutzt werden, um die Dinge ein wenig genauer zu kontrollieren. Der Grund, warum ich kurze Namen für meine Globals-Module verwende, besteht darin, den Code nicht zu sehr mit Vorkommnissen zu überladen. Mit nur ein wenig Erfahrung werden sie mit nur 1 oder 2 Zeichen mnemonisch genug.

Es ist immer noch möglich, z. B. g.x eine Zuweisung zu machen, wenn x noch nicht in g definiert wurde, und ein anderes Modul kann dann auf g.x zugreifen. Auch wenn der Dolmetscher es erlaubt, ist dieser Ansatz nicht so transparent, und ich vermeide es. Es besteht immer noch die Möglichkeit, versehentlich eine neue Variable in g zu erstellen, die auf einen Tippfehler im Variablennamen für eine Zuweisung zurückzuführen ist. Manchmal ist eine Untersuchung von dir (g) nützlich, um irgendwelche Überraschungsnamen zu entdecken, die durch einen solchen Unfall entstanden sein könnten.

+5

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

0

Ich konnte erreichen Quer Modul modifizierbaren (oder veränderbare ) Variablen, die durch ein Wörterbuch verwenden:

# 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 

Wenn test_wait_app_up_fail startet, ist die tatsächliche Zeitüberschreitungsdauer beträgt 3 Sekunden.

2

Ich wollte eine Antwort schreiben, dass es einen Fall gibt, wo die Variable nicht gefunden wird.

Zyklische Importe können das Modulverhalten beeinträchtigen.

Zum Beispiel:

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 

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