Frage

Gibt es eine sinnvolle Unterscheidung zwischen:

class A(object):
    foo = 5   # some default value

vs.

class B(object):
    def __init__(self, foo=5):
        self.foo = foo

Wenn Sie viele Instanzen erstellen, gibt es einen Unterschied in der Leistung oder den Platzbedarf für die beiden Arten? Wenn Sie den Code lesen, haben Sie die Bedeutung der beiden Arten betrachten signifikant verschieden sein?

War es hilfreich?

Lösung

Darüber hinaus Überlegungen zur Leistung, gibt es einen signifikanten semantischen Unterschied. In der Klasse Attribut Fall gibt es nur ein Objekt bezeichnet. In der Instanz-Attribut-Set-at-Instanziierung, es kann auf mehrere Objekte bezeichnet werden. Zum Beispiel

>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
...  def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo    
[]

Andere Tipps

Der Unterschied besteht darin, dass das Attribut auf der Klasse von allen Instanzen gemeinsam genutzt wird. Das Attribut auf einer Instanz ist einzigartig für diese Instanz.

Wenn von C ++ kommen, Attribute für die Klasse sind mehr wie statische Member-Variablen.

Hier ist ein sehr guter Post und Zusammenfassung als unten.

class Bar(object):
    ## No need for dot syntax
    class_var = 1

    def __init__(self, i_var):
        self.i_var = i_var

## Need dot syntax as we've left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)

## Finds i_var in foo's instance namespace
foo.i_var
## 2

## Doesn't find class_var in instance namespace…
## So look's in class namespace (Bar.__dict__)
foo.class_var
## 1

Und in visueller Form

eingeben Bildbeschreibung hier

Klasse Attribut Zuordnung

  • Wenn ein Klassenattribut durch Zugriff auf die Klasse festgelegt ist, wird der Wert für außer Kraft setzen alle Instanzen

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
    
  • Wenn eine Klassenvariable durch den Zugriff auf eine Instanz festgelegt ist, wird der Wert außer Kraft setzen nur für diese Instanz . Dies resultiert im Wesentlichen überschreibt die Klassenvariable und wandelt es in eine Instanz Variable zur Verfügung, intuitiv, nur für diese Instanz .

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1
    

Wann möchten Sie verwenden Klassenattribut?

  • Speichern von Konstanten . Als Klassenattribut kann als Attribut der Klasse selbst zugegriffen werden kann, ist es oft schön, sie für die Speicherung der Klasse weit klassenspezifische Konstanten zu verwenden,

    class Circle(object):
         pi = 3.14159
    
         def __init__(self, radius):
              self.radius = radius   
        def area(self):
             return Circle.pi * self.radius * self.radius
    
    Circle.pi
    ## 3.14159
    c = Circle(10)
    c.pi
    ## 3.14159
    c.area()
    ## 314.159
    
  • Definieren von Standardwerten . Als triviales Beispiel könnten wir eine begrenzte Liste (das heißt, eine Liste, die eine bestimmte Anzahl von Elementen oder weniger nur halten können) erstellen, und wählen Sie eine Standard-Obergrenze von 10 Stück haben

    class MyClass(object):
        limit = 10
    
        def __init__(self):
            self.data = []
        def item(self, i):
            return self.data[i]
    
        def add(self, e):
            if len(self.data) >= self.limit:
                raise Exception("Too many elements")
            self.data.append(e)
    
     MyClass.limit
     ## 10
    

Da die Menschen in den Kommentaren hier und in zwei weiteren Fragen als dups markiert alle in der gleichen Art und Weise, dies zu sein verwirrt erscheinen, ich denke, es lohnt sich das Hinzufügen einer zusätzlichen Antwort oben auf Alex Coventry .

Die Tatsache, dass Alex einen Wert eines wandelbaren Typ zuweist, wie eine Liste, hat nichts damit zu tun, ob die Dinge gemeinsam genutzt werden oder nicht. Wir können dies mit der Funktion id sehen oder is Operator:

>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
...     def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False

(Wenn Sie sich fragen, warum ich object() anstelle von, sagen wir, 5, dass läuft in zwei ganz andere Probleme zu vermeiden ist, die will ich nicht in hier bekommen, aus zwei verschiedenen Gründen, völlig getrennt -created 5s kann sein die gleiche Instanz der Zahl 5 enden. Aber ganz separat erstellt object()s nicht.)


Also, warum ist es, dass a.foo.append(5) in Alex Beispiel wirkt b.foo, aber a.foo = 5 in meinem Beispiel nicht? Nun, versuchen a.foo = 5 in Alex Beispiel, und feststellen, dass es dort nicht Affekt b.foo entweder .

a.foo = 5 wird nur machen a.foo in einen Namen für 5. Das wirkt sich nicht auf b.foo oder einen anderen Namen für den alten Wert, der a.foo verwendet zu verweisen. * Es ist ein wenig kompliziert, dass wir eine Instanz Attribut erstellen, von einem Klassenattribut verbirgt, ** aber sobald man sich, dass nichts kompliziert geschieht hier.


Hoffentlich ist es jetzt klar, warum Alex eine Liste verwendet: die Tatsache, dass Sie eine Liste mutieren können, bedeutet es einfacher ist, zu zeigen, dass zwei Variablen die gleiche Liste nennen, und bedeutet auch, noch wichtiger ist es in realen Code zu wissen, ob Sie zwei Listen oder zwei Namen für die gleiche Liste.


* Die Verwirrung für die Menschen aus einer Sprache wie C ++ kommen, ist, dass in Python, Werte werden in Variablen gespeichert. Werte leben in wert Land, auf ihrem eigenen, Variablen sind nur Namen für Werte aus und Zuordnung erzeugt nur einen neuen Namen für einen Wert. Wenn es hilft, denken Sie an jedem Python Variable als shared_ptr<T> anstelle einem T.

** nehmen Manche Menschen nutzen diese durch ein Klasse-Attribut als „Standardwert“ für eine Instanz mit Attribute, das Fällen kann oder nicht gesetzt werden. Dies kann in einigen Fällen sinnvoll sein, es kann aber auch verwirrend sein, so mit ihm vorsichtig sein.

Es gibt eine weitere Situation.

Klassen- und Instanzattribute ist Descriptor .

# -*- encoding: utf-8 -*-


class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        return self.val


class Base(object):
    attr_1 = RevealAccess(10, 'var "x"')

    def __init__(self):
        self.attr_2 = RevealAccess(10, 'var "x"')


def main():
    b = Base()
    print("Access to class attribute, return: ", Base.attr_1)
    print("Access to instance attribute, return: ", b.attr_2)

if __name__ == '__main__':
    main()

Vor AUSGABE:

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

Die gleiche Art von Instanz Zugriff durch Klasse oder Instanz zurückgeben anderes Ergebnis!

Und ich fand in c.PyObject_GenericGetAttr Definition , und eine große Post .

Erklären Sie

  

Wenn das Attribut im Wörterbuch der Klassen gefunden wird, die bis zu machen.   die Objekte MRO, dann überprüfen, um zu sehen, ob das Attribut auf einen Data Descriptor nachgeschlagen wird (was nichts anderes, dass eine Klasse ist, sowohl die __get__ Durchführung und Umsetzung der __set__ Methoden).   Ist dies der Fall, beheben Sie das Attribut-Lookup durch die __get__ Methode des Data Descriptor Aufruf (Zeilen 28 bis 33).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top