如何确保没有强力打字的稳定合同?

时间:2015-09-24 07:56:26

标签: python python-2.7 types duck-typing dynamic-typing

get_min_length()接受的参数必须与get_pkt_type()的可能返回值匹配:

def get_pkt_type(some_val):
    """Determine the type of an XCP packet.

    :return:
        'CMD' if "Command" packet,
        'RES' if "Command Response" packet,
        'ERR' if "Error" packet,
        'CMD/RES' if uncertain whether a CONNECT CMD or a
            RES packet, as their frame bytes can look identical.
    :rtype: str
    """
    if something:
        return 'CMD'
    elif something_else2:
        return 'RES'
    elif something_else3:
        return 'ERR'
    elif something_else4:
        return 'CMD/RES'

def get_min_length(packet_type):
    if packet_type in ['CMD', 'RES']:
        return 4
    elif packet_type in ['ERR', 'CMD/RES']:
        return 6

packet_type = get_pkt_type(some_val)
length = get_min_length(packet_type)

如果程序员将新的数据包类型返回值添加到get_pkt_type(),我如何确保他也不会忘记将值添加到get_min_length()。在强类型语言中,packet_type将是一个返回并传递的已定义类型,因此我会保持安全。

4 个答案:

答案 0 :(得分:1)

通常,在Python中,如果您有一组可扩展的值,那么建立一个浅层继承层次结构是有意义的。这不容易忘记。对于固定的值集,枚举更好。

那就是说,你应该做的第一件事是尾随

raise ValueError("Unexpected enum value")

到您的职能部门。

您可能会考虑使用字典来表示此类映射:

pkt_lengths = {
    'CMD': 4,
    'RES': 4,
    'ERR': 6,
    'CMD/RES': 6,
}

get_min_length = pkt_lengths.__getitem__

然后您可以添加一个简单的测试

packet_types = {'CMD', 'RES', 'ERR', 'CMD/RES'}
assert not packet_types.symmetric_difference(pkt_lengths)

如果您经常这样做,请构建一个函数:

def enum_mapper(enum, mapper):
    assert not enum.symmetric_difference(mapper)
    return mapper.__getitem__

可以让你做到

get_min_length = enum_mapper(packet_types, pkt_lengths)

并在启动时获得检查。

另外,请考虑使用正确的enum.Enum

答案 1 :(得分:1)

即使你可以,你也不应该这样做。 Python是一种Duck-Typed语言,使用duck-typed语言,我们不会根据类型限制变量。考虑我定义自定义packet_type的情况如下: -

class SpecialPacket(object):

    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other

special_packet = SpecialPacket('CMD')

print(get_min_length(special_packet))
# 6

这就是duck-typing的美妙之处,您添加了一个新数据包,而您根本不需要更改代码。

回答你的问题 - 写测试

这就是为什么测试在动态和弱类型语言中被认为非常重要的原因。我们不依赖于强类型,最终编写了比我们需要的代码更多的代码,而是在所有阶段对代码进行了大量测试。添加新数据包的个人有责任确保他的代码有效,并且他最终不会破坏您的代码。

答案 2 :(得分:1)

你实际上可以在python中创建自己的类型 - 类型只是类。

class packet_type(object):
  def __init__(self, name, length):
    self.name = name
    self.length = length

CMD = packet_type("CMD", 4)

有时,这看起来有点沉重。更简单的替代方法是将属性转储到原始数据结构中,如Veedrac的答案。 如果你只想要一个类似结构的容器,那么非常好的中间地带是namedtuples

from collections import namedtuple
packet_type = namedtuple('packet_type', ['name', 'length']
CMD = packet_type("CMD", 4)

所有这些选项的优点是参数直接在对象上定义,它们实际属于它们。这意味着只有一个点需要定义新参数(在新实例上),而不是在您的设置中。对于鸭子打字,它也可以更好地使用,因为任何具有namelength的课程都可以使用。

答案 3 :(得分:0)

没有办法确保程序员不会忘记任何事情。程序员是人,人往往会忘记事物。

但是,您可以向get_min_length(packet_type)添加一些代码,从而提醒程序员将新值添加到此函数中。

else:
    print "Unknown packet type. Please add new value to the list."
    return some_int_value

或者您甚至可以引发异常,因此程序员立即看到错误。

PS。在Python中,您必须在调用它们之前定义您的函数。