13 Stimmen

Python Klasse vs. Modul Attribute

Ich bin daran interessiert, einige Diskussion über Klasse Attribute in Python zu hören. Zum Beispiel, was ist ein guter Anwendungsfall für Klassenattribute? Im Großen und Ganzen kann ich mir keinen Fall vorstellen, in dem ein Klassenattribut einem Attribut auf Modulebene vorzuziehen ist. Wenn das wahr ist, warum gibt es sie dann?

Das Problem, das ich mit ihnen habe, ist, dass es fast zu einfach ist, einen Klassenattributwert versehentlich zu verletzen, und dann hat sich Ihr "globaler" Wert in ein lokales Instanzattribut verwandelt.

Bitte kommentieren Sie, wie Sie die folgenden Situationen angehen würden:

  1. Konstante Werte, die von einer Klasse und/oder Unterklassen verwendet werden. Dazu können Wörterbuchschlüssel mit "magischen Zahlen" oder Listenindizes gehören, die sich nie ändern, aber möglicherweise einmalig initialisiert werden müssen.
  2. Standardklassenattribut, das in seltenen Fällen für eine spezielle Instanz der Klasse aktualisiert wird.
  3. Globale Datenstruktur zur Darstellung des internen Zustands einer Klasse, die von allen Instanzen gemeinsam genutzt wird.
  4. Eine Klasse, die eine Reihe von Standardattributen initialisiert, die nicht durch Konstruktorargumente beeinflusst werden.

Einige verwandte Beiträge:
Unterschied zwischen Klassen- und Instanzattributen

7voto

Glenn Maynard Punkte 53282

4: I niemals Klassenattribute verwenden, um Standardinstanzattribute zu initialisieren (die, die Sie normalerweise in __init__ ). Zum Beispiel:

class Obj(object):
    def __init__(self):
        self.users = 0

und niemals:

class Obj(object):
    users = 0

Warum? Weil es inkonsistent ist: Es tut nicht, was Sie wollen, wenn Sie etwas anderes als ein unveränderliches Objekt zuweisen:

class Obj(object):
    users = []

bewirkt, dass die Benutzerliste für alle Objekte gemeinsam genutzt wird, was in diesem Fall nicht erwünscht ist. Es ist verwirrend, diese in Klassenattribute und Zuweisungen in __init__ je nach Typ, deshalb lege ich sie immer alle in __init__ was ich ohnehin klarer finde.


Was den Rest betrifft, so füge ich im Allgemeinen klassenspezifische Werte innerhalb der Klasse ein. Das liegt nicht so sehr daran, dass Globals "böse" sind - sie sind keine so große Sache wie in anderen Sprachen, weil sie immer noch auf das Modul skaliert sind, es sei denn, das Modul selbst ist zu groß - aber wenn externer Code auf sie zugreifen will, ist es praktisch, alle relevanten Werte an einem Ort zu haben. Zum Beispiel, in module.py:

class Obj(object):
    class Exception(Exception): pass
    ...

und dann:

from module import Obj

try:
    o = Obj()
    o.go()
except o.Exception:
    print "error"

Abgesehen davon, dass Unterklassen den Wert ändern können (was ohnehin nicht immer erwünscht ist), bedeutet es, dass ich nicht mühsam Ausnahmenamen und eine Menge anderer Dinge importieren muss, die für die Verwendung von Obj erforderlich sind. "from module import Obj, ObjException, ..." wird schnell ermüdend.

4voto

ilya n. Punkte 17572

Was ist ein guter Anwendungsfall für Klassenattribute?

Fall 0. Klassenmethoden sind lediglich Klassenattribute. Dies ist nicht nur eine technische Ähnlichkeit - Sie können auf Klassenmethoden zur Laufzeit zugreifen und sie ändern, indem Sie ihnen Callables zuweisen.

Fall 1. Ein Modul kann leicht mehrere Klassen definieren. Es ist sinnvoll, alles zu kapseln, was mit class A en A... und alles über class B en B... . Zum Beispiel,

# module xxx
class X:
    MAX_THREADS = 100
    ...

# main program
from xxx import X

if nthreads < X.MAX_THREADS: ...

Fall 2. Diese Klasse hat viele Standardattribute, die in einer Instanz geändert werden können. Hier ist die Möglichkeit, ein Attribut als "globale Voreinstellung" zu belassen, eine Funktion und kein Fehler.

class NiceDiff:
    """Formats time difference given in seconds into a form '15 minutes ago'."""

    magic = .249
    pattern = 'in {0}', 'right now', '{0} ago'

    divisions = 1

    # there are more default attributes

Man erstellt eine Instanz von NiceDiff, um die bestehende oder leicht geänderte Formatierung zu verwenden, aber ein Lokalisierer für eine andere Sprache unterteilt die Klasse, um einige Funktionen auf eine grundlegend andere Weise zu implementieren Konstanten umdefinieren:

class (NiceDiff): # NiceDiff localized to Russian
    '''   ,  -300,   '5  '.'''

    pattern = ' {0}', ' ', '{0} '

Ihre Fälle :

  • Konstanten - ja, ich habe sie in den Unterricht aufgenommen. Es ist seltsam zu sagen self.CONSTANT = ... Ich sehe also kein großes Risiko, sie zu verprügeln.
  • Standardattribut -- gemischt, kann wie oben an die Klasse gehen, kann aber auch an __init__ abhängig von der Semantik.
  • Globale Datenstruktur --- geht an die Klasse, wenn verwendet seulement durch die Klasse, kann aber auch an ein Modul gehen, muss aber in jedem Fall très gut dokumentiert.

2voto

Martin v. Löwis Punkte 120025

Klassenattribute werden häufig verwendet, um das Überschreiben von Standardwerten in Unterklassen zu ermöglichen. BaseHTTPRequestHandler hat zum Beispiel die Klassenkonstanten sys_version und server_version, wobei letztere standardmäßig auf "BaseHTTP/" + __version__ . SimpleHTTPRequestHandler überschreibt server_version mit "SimpleHTTP/" + __version__ .

1voto

Eric O Lebigot Punkte 85676

Kapselung ist ein guter Grundsatz: Wenn ein Attribut innerhalb der Klasse liegt, zu der es gehört, anstatt im globalen Bereich zu liegen, gibt dies den Lesern des Codes zusätzliche Informationen.

In Ihren Situationen 1-4 würde ich daher Globals so weit wie möglich vermeiden und lieber Klassenattribute verwenden, mit denen man von der Kapselung profitieren kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X