Ich bin neugierig auf eine gute Möglichkeit, Wertobjekt in Python zu definieren. Laut Wikipedia: "value object ist ein kleines Objekt, das eine einfache Entität darstellt, deren Gleichheit nicht auf Identität basiert: d. H. Zwei Wertobjekte sind gleich, wenn sie denselben Wert haben und nicht notwendigerweise dasselbe Objekt sind". In Python bedeutet das im Wesentlichen neu definiert __eq__
und __hash__
Methoden, sowie Unveränderlichkeit.Wie definiert man ein PyCharm-freundliches Wertobjekt in Python?
Standard namedtuple
scheint fast perfekte Lösung mit der Ausnahme, dass sie nicht gut mit modernen Python IDE wie PyCharm spielen. Ich meine, dass die IDE keine hilfreichen Erkenntnisse über die Klasse namedtuple
liefert. Während es möglich ist docstring zu dieser Klasse mit Trick so befestigen:
class Point2D(namedtuple("Point2D", "x y")):
"""Class for immutable value objects"""
pass
es gibt einfach keinen Ort, wo Beschreibung der Konstruktor Argumente setzen und ihre Typen angeben. PyCharm ist schlau genug, Argumente für Point2D
"Konstruktor" zu erraten, aber typenweise ist es blind.
Dieser Code hat irgendeine Art Informationen in gedrückt, aber es ist nicht sehr nützlich:
class Point2D(namedtuple("Point2D", "x y")):
"""Class for immutable value objects"""
def __new__(cls, x, y):
"""
:param x: X coordinate
:type x: float
:param y: Y coordinate
:type y: float
:rtype: Point2D
"""
return super(Point2D, cls).__new__(cls, x, y)
point = Point2D(1.0, 2.0)
PyCharm Typen sehen, wenn neue Objekte konstruieren, wird aber nicht, dass point.x und point.y greift Schwimmer ist, also würde nicht helfen, ihren Missbrauch zu erkennen. Und ich mag auch nicht die Idee, "magische" Methoden routinemäßig neu zu definieren.
Ich suche nach etwas, so dass sein:
- genauso einfach wie normale Python-Klasse oder namedtuple
- bieten Wert Semantik (Gleichheit, Hashes, Unveränderlichkeit)
- leicht zu definieren Dokument in einer Weise, die gut mit IDE
Ideale Lösung könnte so aussehen wie zu spielen:
class Point2D(ValueObject):
"""Class for immutable value objects"""
def __init__(self, x, y):
"""
:param x: X coordinate
:type x: float
:param y: Y coordinate
:type y: float
"""
super(Point2D, self).__init__(cls, x, y)
Oder dass:
class Point2D(object):
"""Class for immutable value objects"""
__metaclass__ = ValueObject
def __init__(self, x, y):
"""
:param x: X coordinate
:type x: float
:param y: Y coordinate
:type y: float
"""
pass
Ich habe versucht, etwas zu finden, aber ohne Erfolg. Ich dachte, dass es ratsam ist, um Hilfe zu bitten, bevor ich es selbst ausführe.
UPDATE: Mit Hilfe von user4815162342 gelang es mir, etwas zu finden, das funktioniert. Hier ist der Code:
class ValueObject(object):
__slots__ =()
def __repr__(self):
attrs = ' '.join('%s=%r' % (slot, getattr(self, slot)) for slot in self.__slots__)
return '<%s %s>' % (type(self).__name__, attrs)
def _vals(self):
return tuple(getattr(self, slot) for slot in self.__slots__)
def __eq__(self, other):
if not isinstance(other, ValueObject):
return NotImplemented
return self.__slots__ == other.__slots__ and self._vals() == other._vals()
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(self._vals())
def __getstate__(self):
"""
Required to pickle classes with __slots__
Must be consistent with __setstate__
"""
return self._vals()
def __setstate__(self, state):
"""
Required to unpickle classes with __slots__
Must be consistent with __getstate__
"""
for slot, value in zip(self.__slots__, state):
setattr(self, slot, value)
Es ist sehr weit von einer idealen Lösung entfernt. Klassendeklaration sieht wie folgt aus:
class X(ValueObject):
__slots__ = "a", "b", "c"
def __init__(self, a, b, c):
"""
:param a:
:type a: int
:param b:
:type b: str
:param c:
:type c: unicode
"""
self.a = a
self.b = b
self.c = c
Es ist insgesamt viermal alle Attribute zur Liste: in __slots__
, in Ctor Argumente, in docstring und in Ctor Körper. Bisher habe ich keine Ahnung, wie ich es weniger peinlich machen soll.
Beachten Sie, dass 'namedtuple' in erster Linie sowohl die Tupel-Schnittstelle (Indexierung, Entpacken) als auch den Zugriff auf Attribute bereitstellt. Es wurde für die Abwärtskompatibilität von Funktionen erfunden, die zum Zurückgeben von Tupeln wie "os.stat" oder "time.gmtime" verwendet wurden. Es ist wahrscheinlich nicht die optimale Wahl für einen einfachen Werttyp. – user4815162342
In Bezug auf Typen: * PyCharm ist schlau genug, Argumente für Point2D "Konstruktor" zu erraten, aber typenweise ist es blind * Vielleicht sollten Sie eine statisch typisierte Sprache verwenden? In Python sollte es nicht so eine große Sache sein, dass eine IDE über Typen blind ist. – user4815162342
Nun, 'namedtuple' machen fast den richtigen Job für mich. Es ist definitiv mehr als ein einfaches Wertobjekt, aber ich kann damit leben. Wie für die Verwendung statisch getippter Sprache, ich wünschte, ich könnte. Aber ich habe ein Python-Projekt an der Hand und suche nach einer Möglichkeit, die Entwicklung komfortabler zu gestalten. Und PyCharm macht schon sehr gute Arbeit, um den Typ der Variablen mit Hilfe von Docstrings zu ermitteln. –