webentwicklung-frage-antwort-db.com.de

Wie muss ich in Python Standardwerte für Instanzvariablen deklarieren?

Sollte ich meinen Klassenmitgliedern Standardwerte wie folgt geben:

class Foo:
    num = 1

oder wie das?

class Foo:
    def __init__(self):
        self.num = 1

In diese Frage habe ich festgestellt, dass in beiden Fällen

bar = Foo()
bar.num += 1

ist eine gut definierte Operation.

Ich verstehe, dass mir die erste Methode eine Klassenvariable gibt, die zweite jedoch nicht. Wenn ich jedoch keine Klassenvariable benötige, sondern nur einen Standardwert für meine Instanzvariablen festlegen muss, sind beide Methoden gleichermaßen gut? Oder einer von ihnen mehr "Pythonic" als der andere?

Mir ist aufgefallen, dass im Django-Tutorial sie die zweite Methode verwenden, um Models zu deklarieren. Ich persönlich denke, die zweite Methode ist eleganter, aber ich würde gerne wissen, was der "Standard" ist.

68
int3

Um die Antwort von bp zu erweitern, wollte ich Ihnen zeigen, was er mit unveränderlichen Typen meinte.

Zunächst ist das okay:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

Dies funktioniert jedoch nur für unveränderliche (unveränderliche) Typen. Wenn der Standardwert veränderbar war (dh er kann ersetzt werden), würde dies stattdessen passieren:

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 

Beachten Sie, dass sowohl a als auch b ein gemeinsames Attribut haben. Dies ist oft unerwünscht.

Dies ist die Pythonic-Methode zum Definieren von Standardwerten für Instanzvariablen, wenn der Typ veränderbar ist:

>>> class TestC():
...     def __init__(self, attr=None):
...         if attr is None:
...             attr = []
...         self.attr = attr
...     
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]

Der Grund für mein erstes Codefragment ist, dass Python bei unveränderlichen Typen immer eine neue Instanz davon erstellt. Wenn Sie 1 zu 1 hinzufügen mussten, erstellt Python eine neue 2, da die alte 1 nicht geändert werden kann. Der Grund ist meistens für das Hashing, glaube ich.

102
Xavier Ho

Die beiden Ausschnitte machen unterschiedliche Dinge, es ist also keine Frage des Geschmacks, sondern des richtigen Verhaltens in Ihrem Kontext. Python-Dokumentation erklärt den Unterschied, aber hier einige Beispiele:

Anlage A

class Foo:
  def __init__(self):
    self.num = 1

Dies bindet num an die Foo -Instanzen. Änderungen an diesem Feld werden nicht an andere Instanzen weitergegeben.

Somit:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1

Anlage B

class Bar:
  num = 1

Dies bindet num an die Bar class. Änderungen werden propagiert!

>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3

Aktuelle Antwort

Wenn ich keine Klassenvariable benötige, sondern nur einen Standardwert für meine Instanzvariablen festlegen muss, sind beide Methoden gleichermaßen gut? Oder einer von ihnen mehr "Pythonic" als der andere?

Der Code in Exponat B ist einfach falsch: Warum sollten Sie ein Klassenattribut (Standardwert bei der Instanzerstellung) an die einzelne Instanz binden?

Der Code in Ausstellung A ist in Ordnung.

Wenn Sie in Ihrem Konstruktor Standardwerte für Instanzvariablen angeben möchten, würde ich jedoch Folgendes tun:

class Foo:
  def __init__(num = None):
    self.num = num if num is not None else 1

...oder auch:

class Foo:
  DEFAULT_NUM = 1
  def __init__(num = None):
    self.num = num if num is not None else DEFAULT_NUM

... oder sogar: (bevorzugt, aber nur wenn es sich um unveränderliche Typen handelt!)

class Foo:
  def __init__(num = 1):
    self.num = num

Auf diese Weise können Sie:

foo1 = Foo(4)
foo2 = Foo() #use default
45
badp

Die Verwendung von Klassenmitgliedern für die Angabe von Standardwerten funktioniert sehr gut, solange Sie nur mit unveränderlichen Werten vorgehen. Wenn Sie versuchen, es mit einer Liste oder einem Diktat zu tun, wäre das ziemlich tödlich. Es funktioniert auch, wenn das Instanzattribut eine Referenz auf eine Klasse ist, solange der Standardwert Keine ist.

Ich habe gesehen, dass diese Technik sehr erfolgreich in Repoze eingesetzt wurde, einem Framework, das auf Zope läuft. Der Vorteil hierbei ist nicht nur, dass, wenn Ihre Klasse in der Datenbank gespeichert bleibt, nur die nicht standardmäßigen Attribute gespeichert werden müssen, sondern auch, wenn Sie ein neues Feld in das Schema einfügen müssen. Alle vorhandenen Objekte sehen das neue Feld mit dem Standardwert ohne dass die gespeicherten Daten tatsächlich geändert werden müssen.

Ich finde, es funktioniert auch gut bei der allgemeinen Codierung, aber es ist eine Stilsache. Verwenden Sie, was Sie am glücklichsten sind.

3
Duncan

Die Verwendung von Klassenmitgliedern für Standardwerte von Instanzvariablen ist keine gute Idee, und zum ersten Mal wurde diese Idee überhaupt erwähnt. Es funktioniert in Ihrem Beispiel, kann aber in vielen Fällen fehlschlagen. Wenn der Wert beispielsweise veränderlich ist, ändert sich der Standardwert, wenn er in einer nicht modifizierten Instanz geändert wird:

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]
3
Max Shawabkeh

Sie können auch Klassenvariablen als keine deklarieren, um die Weitergabe zu verhindern. Dies ist nützlich, wenn Sie eine genau definierte Klasse benötigen und AttributeErrors ..__ verhindern möchten. Beispiel:

>>> class TestClass(object):
...     t = None
... 
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>

Auch wenn Sie Standardwerte benötigen:

>>> class TestClassDefaults(object):
...    t = None
...    def __init__(self, t=None):
...       self.t = t
... 
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>

Befolgen Sie natürlich immer noch die Informationen in den anderen Antworten zur Verwendung von veränderlichen vs. unveränderlichen Typen als Standard in __init__.

0
Realistic