[Python] 利用组合类型的特殊方法实现可排序SortedList

本文介绍了如何利用Python的特殊方法创建一个自定义的可排序SortedList。SortedList结合了list的功能并支持排序,通过sorted()方法和key参数实现。add()方法采用二分法确保元素按顺序插入。此外,文章探讨了如何判断对象是否包含特定属性以确定其可调用性。

参考资料:《Python3编程指南》第六章

组合类型的特殊方法

特殊方法使用描述
getitem(self,key)y[k]返回序列y中的第key项或者映射类型y中k为键的值
delitem(self,key)del y[k]删除序列y中的第key项或者映射类型y中k为键的值
iter(self)for x in y:pass返回序列y或者映射类型y得迭代子
len(self)len(y)返回序列y的字符数或者映射类型y的个数
reversed(self)reversed(y)返回序列y中的第key项或者映射类型y的反向迭代子
setitem(self,key,v)y[k] = v将序列中的第k项设置为v或者将映射类型y中k的值设置为v
contains(self,x)x in y如果x在序列y中或者x为映射类型y的键,则返回True

SortedList实现

利用list和上述特殊方法,自定义一个可排序的list,代码如下:


_identify = lambda x: x


class SortedList:
    def __init__(self,sequence=None,key=None):
        """初始化SortedList(参考list的创建方式)
        :param sequence:
        :param key:
        """
        # key用于指定排序规则,如果没有指定,则通过Lambda函数指定自身作为排序规则
        self.__key = key or _identify
        # 如果key是可调用对象(函数和方法),则返回True
        assert hasattr(self.__key, "__call__")
        # SortList()形式
        if sequence is None:
            self.__list = []
        # SortList(SortList)形式
        elif (isinstance(sequence, SortedList) and
              sequence.key == self.__key):
            self.__list = sequence.__list[:]
        # SortList(tuple)形式
        else:
            self.__list = sorted(list(sequence), key=self.__key)

    @property
    def key(self):
        """将传入的key函数作为私有属性,提供只读权限
        """
        return self.__key

    def add(self, value):
        """添加元素
        :param value:
        """
        # 获取插入值的索引
        index = self.__bisect_left(value)
        print("----index:",index)
        if index == len(self.__list):
            self.__list.append(value)
        else:
            self.__list.insert(index, value)

    def __bisect_left(self, value):
        """使用二分法
        """
        # 如果没指定key,则调用_identify(value),如果指定,则调用对应key的方法,相当于回调
        key = self.__key(value)
        left, right = 0, len(self.__list)
        while left < right:
            middle = (left+right) // 2
            if self.__key(self.__list[middle]) < key:
                left = middle+1
            else:
                right = middle

        return left

    def remove(self, value):
        index = self.__bisect_left(value)
        if index < len(self.__list) and self.__list[index] == value:
            del self.__list[index]
        else:
            raise ValueError("{0}.remove(x):x not in list".format(self.__class__.__name__))

    def count(self, value):
        count = 0
        index = self.__bisect_left(value)
        while(index < len(self.__list) and
              self.__list[index] == value):
            index += 1
            count += 1

        return count

    def index(self, value):
        index = self.__bisect_left(value)
        if index < len(self.__list) and self.__list[index] == value:
            return index
        else:
            raise ValueError("{0}.index(x):x not in list.".format(self.__class__.__name__))

    # 支持del L[xx]
    def __delitem__(self, key):
        del self.__list[key]

    # 支持 x = L[k]
    def __getitem__(self, index):
        return self.__list[index]

    def __iter__(self):
        return iter(self.__list)

    def __reversed__(self):
        return reversed(self.__list)

    def __contains__(self, value):
        index = self.__bisect_left(value)
        return index < len(self.__list) and self.__list[index] == value

    def pop(self, index=-1):
        return self.__list.pop(index)

    def clear(self):
        self.__list.clear()

    def __len__(self):
        return len(self.__list)

    def __str__(self):
        return str(self.__list)

    def copy(self):
        return SortedList(self, self.key)


if __name__ == "__main__":
    s = SortedList(("J","S","N","L","I","P"))  # ['I', 'J', 'L', 'N', 'P', 'S']
    s1 = SortedList(s)
    s1.add("M")
    def key(value):   # 指定一个key
        return value
    s3 = SortedList(s1, key)
    print(s1)  # ['I', 'J', 'L', 'M', 'N', 'P', 'S']

关键点和难点

  • 要支持排序,通过sorted()方法实现,同时,必须提供一个key,但这里,如果不提供key值,则默认使用自身为排序规则。
  • add()方法结合了list的append()和insert()方法,实现了插入到特定的位置,保持了顺序。
  • 判断索引位置时,使用的方法__bisect_left()使用二分法实现。
  • __init__()方法中使用了hasattr(obj,"__call__")进行判断:如果作为第一个参数的对象包含一个属性,这个属性和第二个参数相同,那么就返回True,所有可调用对象(函数和方法)都包含一个__call__属性.因此,如果传入一个函数对象,则ok,如果传入的不是一个函数对象,则不ok。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值