使用python实现列表抽象中的类型限制

时间:2015-06-26 05:39:14

标签: python list generics

下面是功能范例中的列表抽象,它将任何类型的数据封装在其表示中。

empty_rlist = None
#Representation - start
#Constructor
def rlist(first, rest):
    return(first, rest)

#Selector
def first(s):
    return s[0]

def rest(s):
    return s[1]

#Representation - end

#Constructor and Selector constitutes ADT(above) that supports below invariant:
#If a recursive list s is constructed from a first element f and a recursive list r, then
#   • first(s) returns f, and
#   • rest(s) returns r, which is a recursive list.

#Usage(interface) -  start
def create_list(first, rest):
    return rlist(first, rest)

def len_rlist(s):
    """Compute the length of the recursive list s"""
    def compute_length(s, length):
        if s is empty_rlist:
            return length
        else:
            return compute_length(rest(s), length + 1)
    return compute_length(s, 0)

def getitem_rlist(s, i):
    """Return the element at index i of recursive list s"""
    if i == 1:
        return first(s)
    else:
        return getitem_rlist(rest(s), i-1)

def count(s, value):
    """Count the occurence of value in the list s """
    def count_occurence(s, value, count):
        if s == empty_rlist:
           return count
        else:
           if first(s) == value:
              return count_occurence(rest(s), value, count + 1)
           else:
              return count_occurence(rest(s), value, count)
    return count_occurence(s, value, 0)

#Usage - end


Lst = empty_rlist
Lst = create_list(4, Lst)
Lst = create_list(3, Lst)
Lst = create_list(1, Lst)
Lst = create_list(1, Lst)
print(count(Lst, 1))

在上面的代码中,提供给此抽象用户的接口是create_list / len_rlist / getitem_rlist / count

问题:

  1. 如何强制传递给接口s / len_rlist / getitem_rlist的参数(count)的对象只是{create_list提供的对象。 1}} interface?

  2. 如何强制执行以上列表抽象存储相同类型的数据?

  3. 注意:实际上,需要从语法角度强制执行这些规则。

3 个答案:

答案 0 :(得分:3)

由于python是动态类型语言,因此您无法在执行前检查类型。但实际上有时需要检查输入参数,返回值。我使用下一个解决方案来完成这项任务:

def accepts(*types):
    """Check input types"""
    #print types
    def check_accepts(f):
        assert len(types) == f.func_code.co_argcount
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                       "arg %r does not match %s" % (a,t)
            return f(*args, **kwds)
        new_f.func_name = f.func_name
        return new_f
    return check_accepts

def returns(rtype):
    """Check returns type"""
    def check_returns(f):
        def new_f(*args, **kwds):
            result = f(*args, **kwds)
            assert isinstance(result, rtype), \
                   "return value %r does not match %s" % (result,rtype)
            return result
        new_f.func_name = f.func_name
        return new_f
    return check_returns


if __name__ == '__main__':
    import types 

    @returns(types.NoneType) #Check None as result
    @accepts(int, (int,float)) #First param int; second int or float
    def func(arg1, arg2):
        #return str(arg1 * arg2)
        pass

    func(1, 2)      

答案 1 :(得分:0)

Python 不是强类型语言。更准确地说,它是一个动态类型。这意味着变量包含具有类型的值,但语言本身永远不会禁止在变量中放入不同类型的值。

a = 1     # a contains an integer value
a = "abc" # a now contains a string value

但是,你有isinstancetype函数可以帮助实现这个要求:你可以影响递归列表的类型,只允许绑定元素和递归列表兼容类型。

完整的规范可能是:

  • 一个rlist存储它接受的元素的类型
  • 可以通过添加isinstance(elt, typ)为真的第一个元素来构建rlist,typ rest 部分的可接受的类型
  • 初始列表可以通过明确表示类型或使用其第一个元素的类型来构造

实现:

class rlist:
    def __init__(self, first, rest=None, typ=None):
        self.first = first
        self.rest = rest
        if rest is None:  # initial creation
            self.typ = type(first) if typ is None else typ
        else:
            if not isinstance(rest, rlist):
                raise TypeError(str(rest) + " not a rlist"
            self.typ = rest.typ
        if not isinstance(first, self.typ):
            raise TypeError(str(first) + "not a " + str(typ))
    # other methods ...

但是当你需要强类型时,你应该想知道Python是否真的是合适的语言 - Java 强类型并且本机支持所有这些。 Python方式更多我接受这个并且只是希望它适合,程序员应该知道他做了什么

答案 2 :(得分:0)

为了强制执行该类型,您必须在构造函数中的某处提供类型作为参数。考虑构建参数化类型构造函数。这是一个例子。

>>> def list_spec_for(type_):
...    empty_rlist = type_()
...    def rlist(first, rest):
...        return (type_(first), rest)
...    return empty_rlist, rlist

>>> empty_rlist, rlist = list_spec_for(int)
>>> empty_rlist
0
>>> rlist(1, empty_rlist)
(1, 0)
>>> rlist("1", empty_rlist)
(1, 0)
>>> rlist("one", empty_rlist)
ValueError: invalid literal for int() with base 10: 'one'

如果接受“1”不适合您的目的,您当然可以在rlist的定义中添加isinstance项检查。