Klassenattributte umbn(__init__ aufruf...was ruft er da auf)

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.
rolgal_reloaded
User
Beiträge: 309
Registriert: Dienstag 24. Oktober 2006, 19:31

Beitragvon rolgal_reloaded » Dienstag 19. Juni 2007, 07:14

@Michael

Ich versteh das schon, nur wie oben gezeigt, lassen sich die Werte der einzelnen Instanzen ohne Einfluss aufeinander ändern.

LG

r_r
grandma
User
Beiträge: 26
Registriert: Mittwoch 30. Mai 2007, 15:04

Beitragvon grandma » Dienstag 19. Juni 2007, 07:22

Ok es ist ein Zopeproblem...

Ich habe das File komplett gelöscht und trotzdem wird noch drauf zugegriffen, ich habe keine Ahnung wo er das noch hernimmt.

Mal im Zopeforum nachfragen...

Danke an alle hilfbereiten hier
Benutzeravatar
Michael Schneider
User
Beiträge: 566
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Beitragvon Michael Schneider » Dienstag 19. Juni 2007, 07:27

Hallo Rolgal!
rolgal_reloaded hat geschrieben:Die Werte können auch nur für eine Instanz geändert werden, wenn es Klassenattribute sind.

Oder überseh ich einen wesentlichen Unterschied:

Code: Alles auswählen

>>> class A:
       var1 = 5
       var2 = 10


Ja, es geht darum, ob prinzipiell dasselbe Objekt an die Instanz weitergegeben wird, oder gleich eine Kopie - also ein anderes Objekt.

Wenn Du dem Namen innerhalb der Instanz ein neues Objekt zuweist, dann trennen sich genau da die Wege des Namens und Du greifst fortan von Klasse und Instanz auf unterschiedliche Objekte zu. Bis dahin referenzierten beide Namen (von Klasse und Instanz) dasselbe Objekt:

Code: Alles auswählen

>>> class A:
...    var = 5
...    
>>> a1, a2 = A(), A()
>>> a1.var, a2.var
(5, 5)
>>> A.var = 10
>>> a1.var, a2.var
(10, 10)
>>> a1.var = 15
>>> a1.var, a2.var
(15, 10)


Wenn Du dem Namen aber kein neues Objekt zuweist, sondern dasselbe Objekt nur modifizierst (im folgenden Beispiel list.append), dann referenzierst Du weiterhin dasselbe Objekt mit demselben Namen:

Code: Alles auswählen

>>> class B:
...    var = [5]
...    
>>> b1, b2 = B(), B()
>>> b1.var, b2.var
([5], [5])
>>> B.var.append(10)
>>> b1.var, b2.var
([5, 10], [5, 10])
>>> b1.var.append(15)
>>> b1.var, b2.var
([5, 10, 15], [5, 10, 15])

Das kann erwünscht sein, aber auch nicht. :-)

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
rolgal_reloaded
User
Beiträge: 309
Registriert: Dienstag 24. Oktober 2006, 19:31

Beitragvon rolgal_reloaded » Dienstag 19. Juni 2007, 07:42

:!: :D

Alles klar, aber wäre es nicht doch besser, die Defaultwerte in __init__ reinzuschreiben. Entspricht das nicht mehr der Idee eines Konstruktors?

Also:

Code: Alles auswählen

class A:
    def __init__(self, var1 = [], var2 = []): #usw.
        self.var1 = var1
        self.var2 = var2



Oder gibt es Fälle wo das wie vom Autor des Threads gezeigt wird besser ist?

LG

rolgal_reloaded
Benutzeravatar
Michael Schneider
User
Beiträge: 566
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Beitragvon Michael Schneider » Dienstag 19. Juni 2007, 07:44

rolgal_reloaded hat geschrieben:@Michael

Ich versteh das schon, nur wie oben gezeigt, lassen sich die Werte der einzelnen Instanzen ohne Einfluss aufeinander ändern.

LG

r_r

Hi rolgal,

leider nicht. Es macht wirklich einen Unterschied, ob bei der Instanzierung einer Klasse eine Referenz auf das Objekt der Klasse übergeben wird, oder eine Kopie. In Deinem Beispiel hatte das Ändern des Integerwertes nur deshalb keinen Einfluss, weil Integerobjekte nicht veränderbar sind und Python nicht dem von der Klasse aus referenzierten Integerobjekt den Wert 5 gegeben hat, sondern statt dessen dem instanzlokalen Namen das (neue) Integerobjekt mit dem Wert 10 zugeordnet hat.
Wie oben gezeigt: bei veränderbaren Objekten geht das nicht - solange Du ihnen kein neues Objekt zuweist. Beachte, dass z.B. "list1 = list1 + [10]" zwar eine neue Liste liefert, diese aber nicht mehr das Objekt list1 ist:
>>> l1 = [5]
>>> l1, id(l1)
([5], 13057200)
>>> l1.append(10)
>>> l1, id(l1)
([5, 10], 13057200)
>>> l1 = l1+[15]
>>> l1, id(l1)
([5, 10, 15], 13024032)

Alle Klarheiten beseitigt? :-)

Grüße,
Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Michael Schneider
User
Beiträge: 566
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Beitragvon Michael Schneider » Dienstag 19. Juni 2007, 07:52

rolgal_reloaded hat geschrieben::!: :D

Alles klar, aber wäre es nicht doch besser, die Defaultwerte in __init__ reinzuschreiben. Entspricht das nicht mehr der Idee eines Konstruktors?

Also:

Code: Alles auswählen

class A:
    def __init__(self, var1 = [], var2 = []): #usw.
        self.var1 = var1
        self.var2 = var2


Es gibt eine Initialisierung der Klasse (i.d.R. beim Programmstart ausgeführt) und je eine Initialisierung pro Instanz. Was Du jetzt geschrieben hast, entspricht [EDIT: ebenfalls nicht (siehe BlackJacks Erklärung unten)] grandmas Vorschlag. Allein der Zweck entscheidet hier, wo man Namen initialisiert. Aber ich finde, Du hast Recht: Konstanten kann man im Namensraum der Klasse definieren. :-)

Grüße,
Michael
Zuletzt geändert von Michael Schneider am Dienstag 19. Juni 2007, 12:39, insgesamt 1-mal geändert.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
rolgal_reloaded
User
Beiträge: 309
Registriert: Dienstag 24. Oktober 2006, 19:31

Beitragvon rolgal_reloaded » Dienstag 19. Juni 2007, 08:20

Danke für die Klarstellung, wieder was wichtiges konkretisiert im Oberstübchen,

liebe Grüße

rolgal_reloaded
BlackJack

Beitragvon BlackJack » Dienstag 19. Juni 2007, 09:37

rolgal_reloaded hat geschrieben:Alles klar, aber wäre es nicht doch besser, die Defaultwerte in __init__ reinzuschreiben. Entspricht das nicht mehr der Idee eines Konstruktors?

Also:

Code: Alles auswählen

class A:
    def __init__(self, var1 = [], var2 = []): #usw.
        self.var1 = var1
        self.var2 = var2


Oder gibt es Fälle wo das wie vom Autor des Threads gezeigt wird besser ist?


Das ist nicht viel anders oder besser als Klassenattribute weil die Defaultwerte, genau wie Klassenattribute nur *einmal* ausgewertet werden, nämlich wenn das ``def __init__(...)`` ausgeführt wird:

Code: Alles auswählen

In [1]: class A(object):
   ...:     def __init__(self, var1=[], var2=[]):
   ...:         self.var1 = var1
   ...:         self.var2 = var2
   ...:

In [2]: x = A()

In [3]: y = A()

In [4]: x.var1.append(42)

In [5]: x.var1
Out[5]: [42]

In [6]: y.var1
Out[6]: [42]

In [7]: x.var1 is y.var1
Out[7]: True


Auch das ist sehr wahrscheinlich nicht das Verhalten was man im allgemeinen Fall gerne hätte.
rolgal_reloaded
User
Beiträge: 309
Registriert: Dienstag 24. Oktober 2006, 19:31

Beitragvon rolgal_reloaded » Dienstag 19. Juni 2007, 10:55

@BlackJack

Hast es wieder geschafft mich zu verwirren bzw. mir klar zu machen, dass mir da etwas ganz und gar nicht so klar ist. :D

Dass sich die Modifizierung von Klassenattributen auf alle Instanzen auswirken kann, wie von Michael gezeigt, erscheint mir logisch.

Aber warum denn in meinem Beispiel. Und warum passiert hier was anderes:

Code: Alles auswählen

>>> class A(object):
   def __init__(self):
      self.var1 = []
      self.var2 = []

      
>>> x = A()
>>> y = A()
>>> x.var1.append(42)
>>> x.var1
[42]
>>> y.var1
[]
>>>


LG

rolgal_reloaded
grandma
User
Beiträge: 26
Registriert: Mittwoch 30. Mai 2007, 15:04

Beitragvon grandma » Dienstag 19. Juni 2007, 11:24

Das wird ja noch richtig interessant hier.

Ich habe auch keine Ahnung warum sich hier in den anderen Objekten etwas ändert.

Ich hab mal den Threadtitel umbenannt...
BlackJack

Beitragvon BlackJack » Dienstag 19. Juni 2007, 12:22

Die Erklärung habe ich eigentlich schon gegeben:

Code: Alles auswählen

class A(object):
    def __init__(self, var1=[]):
        self.var1 = var1
        self.var2 = []


Der Ausdruck hinter dem '=' in der Funktionsdefinition wird ein einziges mal ausgewertet, nämlich dann wenn das ``def`` als Teil der Klassendefinition ausgeführt wird. Das Ergebnis dieses Ausdrucks wird bei jedem Aufruf der Methode an den Namen `var1` gebunden. Das ist immer die selbe Liste.

Der Körper der Funktion wird dagegen bei jedem Aufruf der Funktion abgearbeitet. Das heisst `self.var2` wird jedesmal an eine neue Liste gebunden.
rolgal_reloaded
User
Beiträge: 309
Registriert: Dienstag 24. Oktober 2006, 19:31

Beitragvon rolgal_reloaded » Dienstag 19. Juni 2007, 17:10

BlackJack hat geschrieben:Die Erklärung habe ich eigentlich schon gegeben:


Naja, so wie oben eben nicht, sonst hätte ich (wir) nicht nachgefragt :D

Jetzt ist klar warum das so ist.
Aber ist das eigentlich gut, dass es so ist, oder sollte das nicht anders sein??

Oder anders gesagt: weiss ich jetzt endlich, warum ein Konstruktor wie am Anfang des Threads gezeigt sinnvoll sein kann.

LG

rolgal_reloaded
rolgal_reloaded
User
Beiträge: 309
Registriert: Dienstag 24. Oktober 2006, 19:31

Beitragvon rolgal_reloaded » Mittwoch 20. Juni 2007, 15:36

Also ich finde das immer noch schräg......

Wer noch, oder wer nicht? Und vor allem wenn nicht, W A R U M ???
BlackJack

Beitragvon BlackJack » Mittwoch 20. Juni 2007, 18:19

An der Stelle muss(te) man ein Entscheidung treffen: Werden Default-Werte einmal berechnet wenn die Funktion definiert wird, oder jedesmal wenn die Funktion aufgerufen wird.

Zwei Gründe für's einmalige berechnen:

a) Es muss nur einmal gemacht werden. :-)

b) Berechnen bei jedem Funktionsaufruf kann man auch *in* der Funktion machen, wenn man das braucht.

In den allermeisten Fällen sind die Default-Werte in der Praxis nach meiner Erfahrung sowieso "immutables", da braucht man nur eine Auswertung.
rolgal_reloaded
User
Beiträge: 309
Registriert: Dienstag 24. Oktober 2006, 19:31

Beitragvon rolgal_reloaded » Mittwoch 20. Juni 2007, 18:43

@BlackJack

...was meinst du jetzt mit immutables? - Beispiel?

:D

LG

rolgal_reloaded

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder