Verständnisproblem mit global und import

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
BlackJack

@Darii: Ich schreib den "Stern" immer zum Namen weil sonst so etwas wie ``int* foo, bar;`` (noch) leichter falsch verstanden werden kann.
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

numerix hat geschrieben:Wäre nett, wenn mir jemand folgendes Verhalten erklären könnte.

Code: Alles auswählen

# counter.py

n = 0

def count():
    global n
    n += 1

Code: Alles auswählen

import counter

print counter.n
counter.count()
print counter.n
Liefert wie erwartet erst 0 und dann 1.

Code: Alles auswählen

from counter import *

print n
count()
print n
Liefert - für mich unerwartet - beidesmal 0.
Mal generell:
Man sollte zwischen Bezeichnern, Objekten und deren Referenzen unterscheiden.
Variablen sind Bezeichner für Referenzen, mehr nicht.
"=" Statements sind Definitionen, die einem Bezeichner links vom "=" eine Referenz rechts vom "=" zuweisen. Die Referenz wird hierbei vom Python Environment gemanaged:
Steht zb. rechts auch ein Bezeichner, wird dessen Referenz verwendet, steht dort ein Objekt, wird dort eine Referenz erzeugt, indem ein neues Objekt mit neuer Referenz verwendet wird; dies ist völlig unabhängig davon, ob das Objekt "mutable" ist oder nicht (wobei Referenzen bestehender immutable Objekte hierbei präferiert werden). Für gewisse Objekte wie bei "class" und "def" werden automatisch Bezeichner mit den entsprechenden Referenzen erzeugt, und das zur "Compilezeit" und nicht erst zur Laufzeit! D.h. zB bei Definitionen von Funktionen, dass auch deren Parameter zur Compilzeit schon an Objektreferenzen "gebunden" werden. Sind dies "mutable" Objekte, kann die Vorbelegung der Parameter bei jedem Aufruf ein geändertes Objekt bedeuten!
Die Bezeichner haben eine weitere wichtig Eigenschaft: Namensraum.
Die Sichtbarkeit von Bezeichnern ist "user-managed", d.h. der Programmierer kann Einfluss darauf nehmen, indem er Bezeichner entsprechend klassifiziert (zB mit "global") oder explizit externe Namensräume einbindet (zB über "import"). Dies geschieht ebenfalls zur Compilezeit! Er kann auch Bezeichner aus einem Namensraum entfernen (zB mit "del"); hierbei hat er jedoch keinen Einfluss auf die Speicherallokation der Objekte, diese wird ausschliesslich vom Python Environment gemanaged (GC mittels Referenzcounter).

Okay, was mach dein "global" nun in counter.py:
Es bindet den Bezeichner "n" im Namensraum von "counter" in den lokalen Namensraum von "count" ein. Das zur Compilezeit!
Jede Änderung von "n" in diesem Namensraum ist somit eine Änderung der Referenz von "counter.n".

Was machen nun deine Beispiele:
"import counter" bindet den Bezeichner "counter", der "counter.py" referenziert, in den lokalen Namensraum von "__main__" ein.
Deine "print" Ausgaben sind durch "counter.n" referenziert, also genau die Bezeichner, auf die "counter.count" zugreifft. Alles okay.

"from counter import *" bindet die Bezeichner "n" und "count" in den lokalen Namensraum ein. "n" hält hierbei die gleiche Referenz wie "counter.n".
Deine "print" Ausgaben sind durch "n" referenziert, "count" jedoch operiert im Namensraum "counter.n" mit "n". D.h. die Referenz von "counter.n" ist nicht mehr gleich der von "n", das immer noch seine ursprüngliche Referenz hält (zum Zeitpunkt vom import)

Welche Möglichkeiten hat man, im Namensraum von "__main__" die Referenz von "n" durch "counter.count" zu ändern?

Zwei Beispiele, die jetzt verständlich sein sollten:

Code: Alles auswählen

import counter
from counter import *
print n
count()
n = counter.n
print n
oder

Code: Alles auswählen

### counter.py

n = [0]

def count():
    global n
    n[0] += 1
###


from counter import *
print n[0]
count()
print n[0]
Zuletzt geändert von Qubit am Freitag 21. November 2008, 13:35, insgesamt 1-mal geändert.
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

Hi,
gibt es hier vllt. eine Analogie zu dem Linuxbefehl

Code: Alles auswählen

link [-s]
?
Also ein Hardlink (link) hat soweit ich weiß ja die selbe Inode Nummer wie
die Datei auf die er zeigt ist jedoch nur ein weiterer Name für die Datei.
Ein Softlink (link -s) hat aber doch eine eigene Inodenummer und zeigt wirklich nur auf die Datei, d.h. wenn man ihn anklickt öffnet man eigentlich die Datei auf die er zeigt.
MfG Jonas
BlackJack

Gibt es. Aber ohne das `-s`.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Steht zb. rechts auch ein Bezeichner, wird dessen Referenz verwendet, steht dort ein Objekt, wird dort eine Referenz erzeugt, indem ein neues Objekt mit neuer Referenz verwendet wird
Das stimmt zwar, ist aber leicht verwirrend, da du ja kein Objekt ohne Bezeichner schreiben kannst, es sei denn, es wird durch eine Aktion (Aufrufen, mehrere Objekte bauen irgendwas, was auch immer) erzeugt.

@jonas:

Statt link verwende besser "ln", das hat mehr Funktionen, wird afaik öfter verwendet und kann links zwischen allem kreieren.

Außerdem hat bei mir link überhaupt keine "s" Option, und bekommt wohl nur Hardlinks hin.
Benutzeravatar
jonas
User
Beiträge: 156
Registriert: Dienstag 9. September 2008, 21:03

Hi
@Blackjack:
Bei mir in der Schule (opensuse 10) gibt es einen befehl link -s (?)
@str1442:
ln war mir noch nicht bekannt werde ich aber probieren.

MfG Jonas
PS: SCHÖNES WOCHENENDE!
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

jonas: Frage lesen, welche BlackJack beantwortet hat :wink: Es gibt eine Analogie zu link, nicht zu link -s.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@Qubit: Danke auch an dich für die ausführliche Behandlung des Ausgangsproblems. Um sicher zu gehen, dass ich auch das jetzt verstanden habe, nochmal mit meinen Worten:

Code: Alles auswählen

# counter.py
n = 0
def count():
    global n
    n += 1

Code: Alles auswählen

# test.py
from counter import *
print n
count()
print n
Durch den import wird im globalen Namensraum von test.py eine Referenz auf n und count im Modul counter.py angelegt, d.h. zwei die Namen n und count werden an diese beiden Objekte aus counter.py gebunden. Beim Aufruf von count() wird also das Objekt count aus counter.py aufgerufen. Im lokalen Namensraum von count() wird der Bezeichner n als globaler Bezeichner (des Moduls counter.py) deklariert und damit die Möglichkeit eröffnet, an den globalen Bezeichner n innerhalb des lokalen Namensraums von count() ein neues Objekt zu binden. Genau dies geschieht dann durch die Zuweisung "n+=1": Der Name n wird neu an ein Objekt gebunden (und nicht der Wert des Objekts um 1 erhöht!). Ab diesem Zeitpunkt ist das n in counter.py nicht mehr das n in test.py, welches nämlich nach wie vor an das gleiche Objekt (nämlich die 0) gebunden ist.

Richtig?
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

numerix hat geschrieben: Durch den import wird im globalen Namensraum von test.py eine Referenz auf n und count im Modul counter.py angelegt, d.h. zwei die Namen n und count werden an diese beiden Objekte aus counter.py gebunden. Beim Aufruf von count() wird also das Objekt count aus counter.py aufgerufen. Im lokalen Namensraum von count() wird der Bezeichner n als globaler Bezeichner (des Moduls counter.py) deklariert und damit die Möglichkeit eröffnet, an den globalen Bezeichner n innerhalb des lokalen Namensraums von count() ein neues Objekt zu binden. Genau dies geschieht dann durch die Zuweisung "n+=1": Der Name n wird neu an ein Objekt gebunden (und nicht der Wert des Objekts um 1 erhöht!). Ab diesem Zeitpunkt ist das n in counter.py nicht mehr das n in test.py, welches nämlich nach wie vor an das gleiche Objekt (nämlich die 0) gebunden ist.

Richtig?
Ja! Genau so sehe ich das..
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

BlackJack hat geschrieben:@Darii: Ich schreib den "Stern" immer zum Namen weil sonst so etwas wie ``int* foo, bar;`` (noch) leichter falsch verstanden werden kann.
Kannst du mir erklären, was für eine Logik dahintersteckt?
BlackJack

Ich weiss nicht ob ich die Frage richtig verstehe. Der Stern gehört zum Namen. ``int* foo, bar;`` kann leicht fehlinterpretiert werden, als dass `foo` und `bar` beide vom Typ "Zeiger auf `int`" sind, wo doch nur `foo` diesen Typ hat und `bar` ein normales `int` ist. Wenn beides Zeiger sein sollen müsste es ``int *foo, *bar;`` lauten.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

BlackJack hat geschrieben:Ich weiss nicht ob ich die Frage richtig verstehe.
Ja hast du danke. Ich dachte, dass das * zum Typ gehört(wie es eigentlich auch naheliegend und konsequent wäre).

Code: Alles auswählen

int **foo = new int* // Bei new gehört das Sternchen auf einmal zum Typ
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Seit wann hat C ein ``new``?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Leonidas hat geschrieben:Seit wann hat C ein ``new``?
Kommt doch aufs selbe hinaus

Code: Alles auswählen

int **foo = malloc(sizeof(int*))
Antworten