<p>数据类只是面向存储状态的常规类,而不是包含大量逻辑。每次创建一个主要由属性组成的类时,都会创建一个数据类。</p>
<p><code>dataclasses</code>模块所做的是使创建数据类变得更容易。它帮你处理了很多锅炉板。</p>
<p>当数据类必须是散列的时,这一点尤其重要;这需要一个<code>__hash__</code>方法和一个<code>__eq__</code>方法。如果为了便于调试而添加自定义的<code>__repr__</code>方法,则可能会变得非常冗长:</p>
<pre><code>class InventoryItem:
'''Class for keeping track of an item in inventory.'''
name: str
unit_price: float
quantity_on_hand: int = 0
def __init__(
self,
name: str,
unit_price: float,
quantity_on_hand: int = 0
) -> None:
self.name = name
self.unit_price = unit_price
self.quantity_on_hand = quantity_on_hand
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
def __repr__(self) -> str:
return (
'InventoryItem('
f'name={self.name!r}, unit_price={self.unit_price!r}, '
f'quantity_on_hand={self.quantity_on_hand!r})'
def __hash__(self) -> int:
return hash((self.name, self.unit_price, self.quantity_on_hand))
def __eq__(self, other) -> bool:
if not isinstance(other, InventoryItem):
return NotImplemented
return (
(self.name, self.unit_price, self.quantity_on_hand) ==
(other.name, other.unit_price, other.quantity_on_hand))
</code></pre>
<p>使用<code>dataclasses</code>可以将其减少为:</p>
<pre><code>from dataclasses import dataclass
@dataclass(unsafe_hash=True)
class InventoryItem:
'''Class for keeping track of an item in inventory.'''
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
</code></pre>
<p>同一个类decorator还可以生成比较方法(<code>__lt__</code>,<code>__gt__</code>等)并处理不变性。</p>
<p><code>namedtuple</code>类也是数据类,但默认情况下是不可变的(同时也是序列)。<code>dataclasses</code>在这方面要灵活得多,并且可以很容易地构造成它们可以<a href="https://stackoverflow.com/questions/44287623/a-way-to-subclass-namedtuple-for-purposes-of-typechecking/50369898#50369898">fill the same role as a ^{<cd8>} class</a>。</p>
<p>PEP的灵感来自<a href="http://www.attrs.org/en/stable/" rel="noreferrer">^{<cd11>} project</a>,它可以做更多的事情(包括插槽、验证器、转换器、元数据等)。</p>
<p>如果你想看一些例子,我最近为我的几个<a href="http://adventofcode.com/" rel="noreferrer">Advent of Code</a>解决方案使用了<code>dataclasses</code>,请参见<a href="https://github.com/mjpieters/adventofcode/blob/master/2017/Day%2007.ipynb" rel="noreferrer">day 7</a>、<a href="https://github.com/mjpieters/adventofcode/blob/master/2017/Day%2008.ipynb" rel="noreferrer">day 8</a>、<a href="https://github.com/mjpieters/adventofcode/blob/master/2017/Day%2011.ipynb" rel="noreferrer">day 11</a>和<a href="https://github.com/mjpieters/adventofcode/blob/master/2017/Day%2020.ipynb" rel="noreferrer">day 20</a>的解决方案。</p>
<p>如果要在Python版本<;3.7中使用<code>dataclasses</code>模块,则可以安装<a href="https://pypi.org/p/dataclasses/" rel="noreferrer">backported module</a>(需要3.6)或使用上面提到的<code>attrs</code>项目。</p>