使用python在列表抽象中强制类型限制

使用python在列表抽象中强制类型限制,python,list,generics,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 Sel

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

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\u list
/
len\u rlist
/
getitem\u rlist
/
count

问题:

  • 如何强制传递给接口
    len\u rlist
    /
    getitem\u rlist
    /
    count
    的参数(
    s
    )的对象只不过是
    create\u list
    接口提供的对象

  • 如何强制执行上述列表抽象来存储相同类型的数据


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

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

    a = 1     # a contains an integer value
    a = "abc" # a now contains a string value
    
    但是,您有
    isinstance
    type
    函数可以帮助实现这一要求:您可以将类型影响到递归列表,并且只允许将元素和兼容类型的递归列表绑定在一起

    完整规范可以是:

    • rlist存储它接受的元素的类型
    • 可以通过添加第一个元素来构建rlist,其中
      isinstance(elt,typ)
      为true,而
      typ
      为其余部分的可接受typ
    • 初始列表可以通过明确地给它一个类型或使用它的第一个元素的类型来构造
    实施:

    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的方式更多我接受这一点,只是希望它适合,程序员应该知道他在做什么,因为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)      
    

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

    >>> 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
    检查。

    好吧,强制对象类型不是组织数据的pythonish方法。pythonish方法是duck类型,也就是说,您可以检查[集合中的]对象是否适合您的需要,也就是说它们支持特定任务所需的方法,但通常您不应该关心对象类型。一个可能的例外是数字值有效地存储在空间中memory@user3159253它说:“duck类型的原则是,你不应该关心你拥有什么类型的对象,只关心你是否可以对你的对象执行所需的操作。因此,
    isinstance
    关键字是不受欢迎的。”但是我仍然不清楚duck类型是什么,因为我没有得到这样一句话:
    你不应该关心你拥有的对象是什么类型的-只是你是否可以对你的对象执行所需的操作
    例如,[semi-]正式定义是。duck类型化方法的全部要点是,如果对象支持特定任务所需的操作(具有方法、属性等),则该对象被视为“合适的类型”。这尤其意味着泛型容器根本不应该关心对象类型-它们不执行任何“特定任务”,只对集合执行泛型操作。@user3159253如果
    d={'a':1,'b':2}
    ,则duck类型的原则是
    d
    类型,仅当d支持诸如
    setdefault()
    /
    keys()
    /
    fromkeys()
    等操作时。。。。我对duck打字的理解正确吗?如果是,为什么要用这种术语来表达这样的概念?这种方法的优点是什么?@user3159253,正如它所说:Python映射类型和序列类型“接口”非常模糊,我们根本无法使用duck类型。我上面的解决方案是使用函数范式。@overexchange:使用函数范式满足这种需求的问题是规范部分。在这里,我明确地在一个对象中添加了一个类型,因此很容易使用isinstance。我认为这在函数范式中仍然是可能的,但你必须在两种方式中选择:始终使用rest最后一个元素的类型:即使在添加子类型时,你也可以始终添加下一个原始类型,但你必须转到rlist的末尾,或者使用第一个元素的类型,这一类型更便宜,但一旦添加了子类型,您不能再添加父类型元素。当我读到SO Python页面上的标题时,我总是皱眉。谢谢你说Python不是强类型的。@Pynchia Python是强类型的,因为对象不会改变类型。Python是动态类型的,因为我们传递引用,直到最后一分钟才检查类型。你能通过我的查询吗?@overexchange call
    @accepts(yourtype1,yourtype2)create_list(first,rest)
    这里@accepts将检查你的参数type@overexchange,我理解这个问题,但我不知道python的好解决方案。Java使用诸如
    接口
    抽象类
    泛型
    等语言工具解决了这个问题。对于
    python
    您必须使用测试,实际上是关于参数类型的文档和协议。问题不在FP和OOP中。动态类型语言中的问题。python中的OOP只扩展了基类的功能。但这对类型检查没有帮助。只是python开发人员不必忙于类型。基于OOP的abstracrions是一些任务(如对象建模)的好解决方案。黏液