Sono curioso di trovare un buon modo per definire l'oggetto valore in Python. Per Wikipedia: "value object è un oggetto di piccole dimensioni che rappresenta una semplice entità la cui uguaglianza non è basata sull'identità: cioè due oggetti valore sono uguali quando hanno lo stesso valore, non necessariamente lo stesso oggetto". In Python significa essenzialmente ridefinito i metodi __eq__
e __hash__
, nonché l'immutabilità.Come definire un oggetto valore PyCharm in Python?
Lo standard namedtuple
sembra una soluzione quasi perfetta con l'eccezione che non funzionano bene con i moderni IDE Python come PyCharm. Intendo dire che IDE non fornirà davvero alcuna informazione utile sulla classe definita come namedtuple
. Mentre è possibile allegare docstring a tale classe utilizzando trucco come questo:
class Point2D(namedtuple("Point2D", "x y")):
"""Class for immutable value objects"""
pass
semplicemente non c'è alcun posto dove mettere descrizione di argomenti del costruttore e specificare i loro tipi. PyCharm è abbastanza intelligente da indovinare gli argomenti per il "costruttore" Point2D
, ma dal punto di vista del tipo è cieco.
Questo codice avere alcune informazioni di tipo premuto, ma non è molto utile:
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 vedrà tipi quando costruire nuovi oggetti, ma non capire che point.x e punto.Ý sono galleggianti, quindi non aiuterei a scoprire il loro uso improprio. Inoltre, non mi piace l'idea di ridefinire i metodi "magici" su base routinaria.
Quindi sono alla ricerca di qualcosa che sarà:
- altrettanto facile da definire come normale classe Python o namedtuple
- forniscono semantica di valore (uguaglianza, hash, immutabilità)
- facile da documento in un modo che giocherà bene con IDE
soluzione ideale potrebbe essere la seguente:
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)
O che:
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
ho cercato di trovare qualcosa di simile, ma senza successo. Ho pensato che sarebbe saggio chiedere aiuto prima di implementarlo da solo.
UPDATE: Con l'aiuto dell'utente4815162342 sono riuscito a trovare qualcosa che funzioni. Ecco il codice:
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)
È molto lontano da una soluzione ideale. dichiarazione di classe è simile al seguente:
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
E 'totale quattro volte per elencare tutti gli attributi: in __slots__
, negli argomenti ctor, in docstring e nel corpo ctor. Finora non ho idea di come renderlo meno imbarazzante.
Si noti che 'namedtuple' ha lo scopo principale di fornire sia l'interfaccia tuple (indicizzazione, decompressione), sia l'accesso agli attributi. È stato inventato per la compatibilità con le versioni precedenti delle funzioni utilizzate per restituire tuple, come 'os.stat' o' time.gmtime'. Probabilmente non è la scelta ottimale per un tipo di valore semplice. – user4815162342
Per quanto riguarda i tipi: * PyCharm è abbastanza intelligente da indovinare argomenti per il "costruttore" di Point2D, ma dal punto di vista del tipo è cieco * Forse dovresti usare un linguaggio tipizzato in modo statico? In Python non dovrebbe essere un grosso problema che un IDE sia cieco sui tipi. – user4815162342
Bene, 'namedtuple' fa quasi il lavoro giusto per me. È sicuramente più di un semplice oggetto di valore, ma posso conviverci. Per quanto riguarda l'utilizzo del linguaggio tipizzato staticamente, vorrei poterlo fare. Ma ho un progetto Python sulle mani e sto cercando un modo per rendere lo sviluppo più confortevole. E PyCharm fa già un ottimo lavoro per inferire il tipo di variabili usando le docstring. –