Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Python中定义PyCharm友好的值对象?_Python_Pycharm - Fatal编程技术网

如何在Python中定义PyCharm友好的值对象?

如何在Python中定义PyCharm友好的值对象?,python,pycharm,Python,Pycharm,我对Python中定义值对象的好方法很好奇。根据维基百科:“是一个小对象,表示一个简单实体,其相等性不基于身份:即两个值对象具有相同的值时相等,不一定是相同的对象”。在Python中,这本质上意味着重新定义了\uuuuueq\uuuuu和\uuuuuuuuuuuuuuu散列方法,以及不变性 标准的namedtuple似乎是近乎完美的解决方案,但它们不能很好地与PyCharm这样的现代Python IDE配合使用。我的意思是IDE不会真正提供任何关于定义为namedtuple的类的有用见解。虽然可

我对Python中定义值对象的好方法很好奇。根据维基百科:“是一个小对象,表示一个简单实体,其相等性不基于身份:即两个值对象具有相同的值时相等,不一定是相同的对象”。在Python中,这本质上意味着重新定义了
\uuuuueq\uuuuu
\uuuuuuuuuuuuuuu散列
方法,以及不变性

标准的
namedtuple
似乎是近乎完美的解决方案,但它们不能很好地与PyCharm这样的现代Python IDE配合使用。我的意思是IDE不会真正提供任何关于定义为
namedtuple
的类的有用见解。虽然可以使用以下技巧将docstring附加到此类类:

class Point2D(namedtuple("Point2D", "x y")):
    """Class for immutable value objects"""
    pass
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 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
class X(ValueObject):
  __slots__ = 'a', 'b'
根本没有地方可以放置构造函数参数的描述并指定它们的类型。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在构造新对象时会看到类型,但不会抓住该点。x和点.y是浮动,因此无助于检测它们的误用。我也不喜欢在常规基础上重新定义“魔术”方法

因此,我正在寻找一些将:

  • 与普通Python类或namedtuple一样易于定义
  • 提供值语义(相等、哈希、不变性)
  • 易于使用IDE编写文档
理想的解决方案如下所示:

class Point2D(namedtuple("Point2D", "x y")):
    """Class for immutable value objects"""
    pass
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 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
class X(ValueObject):
  __slots__ = 'a', 'b'
或者:

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
我试图找到这样的东西,但没有成功。我认为在自己实施之前寻求帮助是明智的

更新: 在用户4815162342的帮助下,我设法想出了一些有效的方法。代码如下:

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)

列出所有属性总共需要四次:在
\uuuuuu slots\uuuuu
中,在ctor参数中,在docstring中和在ctor body中。到目前为止,我还不知道如何让它不那么尴尬。

您的要求虽然表达得很仔细,但我还是不太清楚,部分原因是我没有使用PyCharm GUI。但这里有一个尝试:

class ValueObject(object):
    __slots__ = ()

    def __init__(self, *vals):
        if len(vals) != len(self.__slots__):
            raise TypeError, "%s.__init__ accepts %d arguments, got %d" \
                % (type(self).__name__, len(self.__slots__), len(vals))
        for slot, val in zip(self.__slots__, vals):
            super(ValueObject, self).__setattr__(slot, val)

    def __repr__(self):
        return ('<%s[0x%x] %s>'
                % (type(self).__name__, id(self),
                   ' '.join('%s=%r' % (slot, getattr(self, slot))
                            for slot in self.__slots__)))

    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 __setattr__(self, attr, val):
        if attr in self.__slots__:
            raise AttributeError, "%s slot '%s' is read-only" % (type(self).__name__, attr)
        super(ValueObject, self).__setattr__(attr, val)
这将获得一个具体的值类,该类包含两个只读插槽和一个自动生成的构造函数,
\uuuuueq\uuuu
\uuuuuuhash\uuu
。例如:

>>> x = X(1.0, 2.0, 3.0)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in __init__
TypeError: X.__init__ accepts 2 arguments, got 3
>>> x = X(1.0, 2.0)
>>> x
<X[0x4440a50] a=1.0 b=2.0>
>>> x.a
1.0
>>> x.b
2.0
>>> x.a = 10
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 32, in __setattr__
AttributeError: X slot 'a' is read-only
>>> x.c = 10
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 33, in __setattr__
AttributeError: 'X' object has no attribute 'c'
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_vals', 'a', 'b']
>>> x == X(1.0, 2.0)
True
>>> x == X(1.0, 3.0)
False
>>> hash(x)
3713081631934410656
>>> hash(X(1.0, 2.0))
3713081631934410656
>>> hash(X(1.0, 3.0))
3713081631933328131
>x=x(1.0、2.0、3.0)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第5行,在_init中__
TypeError:X.。\uuuu init\uuuuu接受2个参数,得到3个
>>>x=x(1.0,2.0)
>>>x
>>>x.a
1
>>>x.b
2
>>>x.a=10
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第32行,在\uuuu setattr中__
AttributeError:X插槽“a”是只读的
>>>x.c=10
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第33行,在__
AttributeError:“X”对象没有属性“c”
>>>目录(x)
“UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"设置属性","大小","插槽","str","子类钩子","weakref","vals","a","b"
>>>x==x(1.0,2.0)
真的
>>>x==x(1.0,3.0)
假的
>>>散列(x)
3713081631934410656
>>>散列(X(1.0,2.0))
3713081631934410656
>>>散列(X(1.0,3.0))
3713081631933328131
如果需要,您可以使用(大概)为IDE提供类型注释提示的文档字符串定义自己的
\uuuuuu init\uuuuuu

版本3.5中新增的
键入
模块和
命名双倍
在版本3.5中,添加了模块,在其中,您将发现一个完全适合您需要的类

新的 它的工作原理与您预期的一样:

  • 简单类型定义:

    from typing import NamedTuple
    class DownloadableFile(NamedTuple):
        file_path: str
        download_url: str
    
  • 在PyCharm中得到认可:


注意
到今天为止,API仍然处于一个稳定的状态。这意味着当新版本发布时,它不能保证向后兼容。但不希望对接口进行更改。我个人的看法是:考虑到设计的简单性,如果有变化,我相信这将是一个简单的重构;)

更新解决方案:

自python 3.7以来,有一个新的内置模块名为
dataclasses
,其中包含
dataclass

Pycharm支持它并知道如何使用它

它非常适合值对象,因为它已经定义了许多您将为值对象定义的内容,并且语法非常简短:

  • repr
  • eq
  • 通过传递
    freezed=True
例如:

从数据类导入数据类
@数据类(冻结=真)
类Point2D:
x:浮动
y:浮子
NamedTuple
Dataclasses
之间存在差异,其中最显著的一个基于元组,另一个基于dict

在我看来,
dataclass
是创建价值对象的最佳代码生成器


有关更多信息,请继续学习python关于如何使用
dataclass

注意
namedtuple
的主要目的是提供元组接口(索引、解包)和属性访问。它的发明是为了向后兼容用于返回元组的函数,例如
os.stat
time.gmtime
。对于简单的值类型来说,它可能不是最佳选择。关于类型:PyCharm足够聪明,可以猜测Point2D“构造函数”的参数,但就类型而言,它是盲目的,也许你应该使用静态类型语言?在Python中,IDE对类型视而不见并不是什么大不了的事。嗯,
namedtuple
几乎可以为我做正确的工作。它肯定比简单的值对象更简单,但我可以接受它。至于使用静态类型语言,我希望