我很好奇在Python中定义值对象的好方法。每个维基百科:“value object是一个小对象,表示一个简单的实体,它的相等性不基于身份:即当两个值对象具有相同的值时,它们是相等的,不一定是相同的对象。在Python中,本质上意味着重新定义__eq__
和__hash__
方法,以及不变性。如何在Python中定义PyCharm友好的值对象?
标准namedtuple
似乎是几乎完美的解决方案,除了它们不像现在的PyCharm之类的Python IDE那么好用。我的意思是,IDE不会真正提供关于定义为namedtuple
的类的任何有用的见解。虽然可以使用招这样附加文档字符串这样的类:
class Point2D(namedtuple("Point2D", "x y")):
"""Class for immutable value objects"""
pass
有根本没有地方放的构造函数的参数说明,并指定其类型。 PyCharm足够聪明地猜测Point2D
“构造函数”的参数,但类型上它是盲目的。
这段代码有某种类型的信息被推入,但它不是非常有用:
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会看到构建新对象时的类型,但不会把握point.x和point.y是花车,所以不会有助于发现他们的滥用。我也不喜欢重新定义“魔法”方法的想法。
所以我在寻找的东西,这将是:
- 一样简单定义为正常Python类或namedtuple
- 提供值语义(平等,哈希,不变性)
- 容易在某种程度上文件将与IDE发挥很好
的理想解决方案看起来是这样的:
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)
或者说:
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
我试图找到这样的事情,但没有成功。我认为在自己实施之前寻求帮助是明智的。
更新: 在user4815162342的帮助下,我设法想出了一些有效的东西。这里的代码:
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)
这是一个非常理想的解决方案。类的声明看起来是这样的:
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
这是总四次列出所有的属性:在__slots__
,在构造函数的参数,在文档字符串并在构造函数体。到目前为止,我不知道如何让它变得不那么笨拙。
请注意,'namedtuple'的主要目的是提供元组接口(索引,解包)和属性访问。它是为了向后兼容用于返回元组的函数而发明的,例如'os.stat'或'time.gmtime'。对于简单的值类型,它可能不是最佳选择。 – user4815162342
关于类型:* PyCharm足够聪明地猜测Point2D“构造函数”的参数,但是类型上它是盲目的*也许你应该使用静态类型语言?在Python中,IDE不应该是一个大问题,因为IDE对类型是盲目的。 – user4815162342
好吧,''ttple''几乎为我做了正确的工作。这绝对是更简单的价值对象,但我可以忍受它。 至于使用静态类型语言,我希望我可以。但我手上有一个Python项目,并寻找让开发更加舒适的方法。而PyCharm已经在使用docstrings推断变量类型方面做得非常出色。 –