1. 一致性
所谓一致性,我的理解是相类似的东西应该使用相同的方法,例如Python中序列的长度都可以使用len()方法进行处理,字符串的拼接可以使用“+”号来统一实现,这就是一致性的实际案例。那么这种一致性是如何是实现的?
2.数据模型
数据模型其实是对 Python 框架的描述,它规范了这门语言自身构建模块的接口,这些模块包括但不限于序列、迭代器、函数、类和上下文管理器,可以说是Python的基类(object)定义。
Python 解释器碰到特殊的句法时,会使用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以两个下划线开头。例如obj[key]其实python调用的方法是__getitem__方法。
3.案例:一副扑克牌
可以用一个有趣的例子体现数据模型的作用。
需求:定义一副扑克牌,扑克牌有花色和点数两种属性。
import doctest
import collections
# namedtuple是python中一种只定义了属性没有定义方法的类,类似于Java中的Java Bean
Card = collections.namedtuple("Card",['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list("JQKA")
# 列表生成器
suits = 'spades diamonds clubs hearts'.split()
# 字符串默认以‘ ’space进行分割
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, index):
return self._cards[index]
上述的代码已经可以实现一副简单的扑克牌序列的效果,运行代码可以得到
>>> deck = FrenchDeck()
>>> len(deck)
52
>>> deck[0]
Card(rank='2', suit='spades')
>>> deck[-1]
Card(rank='A', suit='hearts')
>>> from random import choice
>>> choice(deck)
Card(rank='3', suit='hearts')
>>> choice(deck)
Card(rank='K', suit='spades')
>>> choice(deck)
Card(rank='2', suit='clubs')
从这个案例中,Python的数据模型可以有两种好处。
- 统一的API调用方法,比如len()方法
- 可以利用标准库的的方法,不用重新造轮子
比如random.choice函数可以直接利用到上面。
>>> from random import choice
>>> choice(deck)
Card(rank='3', suit='hearts')
>>> choice(deck)
Card(rank='K', suit='spades')
>>> choice(deck)
Card(rank='2', suit='clubs')
4. 测试技术
4.1 doctest
在《流畅的python》常常将doctest作为代码的测试工具,其中很多框架的用例也是经常用doctest作为测试,例如Pytorch中经常会把测试用例写在文档中,既能够方便用户了解API的使用,又能够进行检测,例如:
class Linear(Module):
r"""Applies a linear transformation to the incoming data: :math:`y = xA^T + b`
Args:
in_features: size of each input sample
out_features: size of each output sample
bias: If set to ``False``, the layer will not learn an additive bias.
Default: ``True``
Shape:
- Input: :math:`(N, *, H_{in})` where :math:`*` means any number of
additional dimensions and :math:`H_{in} = \text{in\_features}`
- Output: :math:`(N, *, H_{out})` where all but the last dimension
are the same shape as the input and :math:`H_{out} = \text{out\_features}`.
Attributes:
weight: the learnable weights of the module of shape
:math:`(\text{out\_features}, \text{in\_features})`. The values are
initialized from :math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})`, where
:math:`k = \frac{1}{\text{in\_features}}`
bias: the learnable bias of the module of shape :math:`(\text{out\_features})`.
If :attr:`bias` is ``True``, the values are initialized from
:math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})` where
:math:`k = \frac{1}{\text{in\_features}}`
Examples::
>>> m = nn.Linear(20, 30)
>>> input = torch.randn(128, 20)
>>> output = m(input)
>>> print(output.size())
torch.Size([128, 30])
"""
在Examples中部分就是测试用的代码,也可以用作用户看API使用案例,一举两得。
4.2 doctest使用方法
doctest使用方法可以分为在源代码内和再源代码外写入。
源代码内
直接在代码的注释中,注意是在文档中使用这些测试,具体到代码需要加入:
‘>>>‘表示输入
输入后没有’>>>'为期望输出
class Vector:
"""This is the object of a vector
>>> vec = Vector(1,3)
>>> print(vec)
Vector(1, 3)
"""
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
当需要测试时,使用:
if __name__ == '__main__':
import doctest
doctest.testmod()
没有错误就没有问题抛出
源代码外
直接将命令行的代码写到文本文件如(testfile)中,而后调用
python -m doctest -f testfile.txt
测试文件样例
>>> from card_set import FrenchDeck, Card
>>> beer_card = Card('7', 'diamonds')
>>> beer_card
Card(rank='7', suit='diamonds')
>>> deck = FrenchDeck()
>>> len(deck)
52
>>> deck[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
>>> deck[12::13]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
>>> Card('Q', 'hearts') in deck
True