Seite 2 von 2

Verfasst: Freitag 21. November 2008, 10:20
von BlackJack
@Darii: Ich schreib den "Stern" immer zum Namen weil sonst so etwas wie ``int* foo, bar;`` (noch) leichter falsch verstanden werden kann.

Verfasst: Freitag 21. November 2008, 12:41
von Qubit
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]

Analogie?

Verfasst: Freitag 21. November 2008, 13:02
von jonas
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

Verfasst: Freitag 21. November 2008, 13:37
von BlackJack
Gibt es. Aber ohne das `-s`.

Verfasst: Freitag 21. November 2008, 14:44
von str1442
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.

Verfasst: Freitag 21. November 2008, 15:35
von jonas
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!

Verfasst: Freitag 21. November 2008, 15:47
von Rebecca
jonas: Frage lesen, welche BlackJack beantwortet hat :wink: Es gibt eine Analogie zu link, nicht zu link -s.

Verfasst: Freitag 21. November 2008, 16:13
von numerix
@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?

Verfasst: Freitag 21. November 2008, 16:21
von Qubit
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..

Verfasst: Samstag 22. November 2008, 14:25
von Darii
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?

Verfasst: Samstag 22. November 2008, 15:24
von 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.

Verfasst: Samstag 22. November 2008, 15:55
von Darii
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

Verfasst: Samstag 22. November 2008, 16:50
von Leonidas
Seit wann hat C ein ``new``?

Verfasst: Samstag 22. November 2008, 17:08
von Darii
Leonidas hat geschrieben:Seit wann hat C ein ``new``?
Kommt doch aufs selbe hinaus

Code: Alles auswählen

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