2008-10-15 3 views
18

Das folgende scheint seltsam .. Grundsätzlich scheint das Somedata-Attribut zwischen allen Klassen, die von the_base_class geerbt hat.Warum wirken sich Attributreferenzen bei der Python-Vererbung so aus?

class the_base_class: 
    somedata = {} 
    somedata['was_false_in_base'] = False 


class subclassthing(the_base_class): 
    def __init__(self): 
      print self.somedata 


first = subclassthing() 
{'was_false_in_base': False} 
first.somedata['was_false_in_base'] = True 
second = subclassthing() 
{'was_false_in_base': True} 
>>> del first 
>>> del second 
>>> third = subclassthing() 
{'was_false_in_base': True} 

definieren self.somedata in der __init__ Funktion ist offensichtlich der richtige Weg, dies zu umgehen (so jede Klasse hat eine eigene ist somedata dict) - aber wann ist ein solches Verhalten wünschenswert?

Antwort

23

Sie haben Recht, somedata zwischen allen Instanzen der Klasse geteilt und es ist Subklassen, weil es bei Klasse Definition Zeit erstellt wird. Die Linien

somedata = {} 
somedata['was_false_in_base'] = False 

ausgeführt werden, wenn die Klasse definiert ist, das heißt, wenn der Interpreter die class Aussage trifft - nicht, wenn die Instanz (man denke statische Initialisierer Blöcke in Java) erstellt wird. Wenn ein Attribut in einer Klasseninstanz nicht existiert, wird das Klassenobjekt auf das Attribut überprüft.

Bei Klassendefinition der Zeit können Sie arbiträren Code wie folgt aus:

import sys 
class Test(object): 
    if sys.platform == "linux2": 
     def hello(self): 
       print "Hello Linux" 
    else: 
     def hello(self): 
       print "Hello ~Linux" 

Auf einem Linux-System, Test().hello()Hello Linux gedruckt wird, auf allen anderen Systemen die andere Zeichenfolge gedruckt wird.

In constrast, Objekte in __init__ an Instanziierung Zeit und gehören zum Beispiel nur erstellt (wenn sie zu self zugeordnet sind):

class Test(object): 
    def __init__(self): 
     self.inst_var = [1, 2, 3] 

Objekte auf einem Klassenobjekt definiert anstatt Instanz kann sein nützlich in vielen Fällen. Zum Beispiel könnten Sie Instanzen Ihrer Klasse cachen, so dass Fälle mit den gleichen Elementwerten können gemeinsam genutzt werden (vorausgesetzt, sie unveränderlich sein soll):

class SomeClass(object): 
    __instances__ = {} 

    def __new__(cls, v1, v2, v3): 
     try: 
      return cls.__insts__[(v1, v2, v3)] 
     except KeyError: 
      return cls.__insts__.setdefault(
       (v1, v2, v3), 
       object.__new__(cls, v1, v2, v3)) 

Meistens ich verwenden, um Daten in der Klasse Körper in Verbindung mit Metaklassen oder generischen Factory-Methoden.

+1

Wie deklariert man dann eine Instanz var? – OscarRyz

+0

Ich habe ein Beispiel in meiner Antwort hinzugefügt. –

+0

Gibt es einen Unterschied zwischen einfachen Daten und komplexen? wie sagt TimB? – OscarRyz

10

Hinweis, dass ein Teil des Verhaltens ist Sie sehen aufgrund somedata ein dict zu sein, wie auf einen einfachen Datentyp wie ein bool gegenüber.

Zum Beispiel finden Sie in diesem anderen Beispiel die unterschiedlich verhält (obwohl sehr ähnlich):

class the_base_class: 
    somedata = False 

class subclassthing(the_base_class): 
    def __init__(self): 
     print self.somedata 


>>> first = subclassthing() 
False 
>>> first.somedata = True 
>>> print first.somedata 
True 
>>> second = subclassthing() 
False 
>>> print first.somedata 
True 
>>> del first 
>>> del second 
>>> third = subclassthing() 
False 

Der Grund dieses Beispiel unterschiedlich von dem in der Frage gegeben verhält, weil hier first.somedata einen neuen Wert gegeben wird (das Objekt True), während im ersten Beispiel das dict -Objekt modifiziert wird, das durch first.somedata (und auch durch die anderen Unterklasseninstanzen) referenziert wird.

Siehe Torsten Mareks Kommentar zu dieser Antwort für weitere Klarstellung.

+0

Wollen Sie sagen, dass einfache Datentypen nicht gemeinsam genutzt werden, während komplexe? – OscarRyz

+0

Nein, sie sind freigegeben, jeder Datentyp in Python ist ein Referenztyp, sogar ganze Zahlen und Booleans usw. Der Grund ist, dass first.somedata nicht den Wert False/True enthält, es verweist auf das Objekt False/True. Wenn es neu zugewiesen wird, verweist es einfach auf ein anderes Objekt. –

+0

@Oscar, ich vermute, der Unterschied liegt an der Art der Referenzen. Wenn Somedata ein Wörterbuch ist, haben erste und zweite eine eigene Referenz, aber jede Referenz verweist auf dasselbe Wörterbuch im Speicher. Wenn ein Boolescher Wert bei Somedate ist, erhalten sie immer noch ihre eigenen Kopien, aber sie modifizieren den Bool direkt. –

2

Ich denke, der einfachste Weg, dies zu verstehen (so dass Sie Verhalten vorhersagen können) ist zu erkennen, dass Ihr somedata ein Attribut der Klasse und nicht die Instanz dieser Klasse ist, wenn Sie es so definieren.

Es gibt wirklich nur eine somedata zu jeder Zeit, weil Sie in Ihrem Beispiel diesen Namen nicht zugewiesen, sondern ihn verwendet haben, um ein Diktat nachzuschlagen und ihm dann ein Element (Schlüssel, Wert) zuzuweisen. Es ist ein Fehler, der eine Konsequenz davon ist, wie der Python-Interpreter funktioniert und kann auf den ersten Blick verwirrend sein.

Verwandte Themen