乐趣区

关于python:Python进阶自定义对象实现切片功能

切片是 Python 中最迷人最弱小最 Amazing 的语言个性(简直没有之一),在《Python 进阶:切片的误区与高级用法》中,我介绍了切片的根底用法、高级用法以及一些应用误区。这些内容都是基于原生的序列类型(如字符串、列表、元组……),那么,咱们是否能够定义本人的序列类型并让它反对切片语法呢?更进一步,咱们是否能够自定义其它对象(如字典)并让它反对切片呢?

1、魔术办法:__getitem__()
想要使自定义对象反对切片语法并不难,只须要在定义类的时候给它实现魔术办法 __getitem__() 即可。所以,这里就先介绍一下这个办法。

语法:object.__getitem__(self, key)

官网文档释义:Called to implement evaluation of self[key]. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence type) is up to the __getitem__() method. If key is of an inappropriate type, TypeError may be raised; if of a value outside the set of indexes for the sequence (after any special interpretation of negative values), IndexError should be raised. For mapping types, if key is missing (not in the container), KeyError should be raised.

概括翻译一下:__getitem__() 办法用于返回参数 key 所对应的值,这个 key 能够是整型数值和切片对象,并且反对正数索引;如果 key 不是以上两种类型,就会抛 TypeError;如果索引越界,会抛 IndexError;如果定义的是映射类型,当 key 参数不是其对象的键值时,则会抛 KeyError。

2、自定义序列实现切片性能
接下来,咱们定义一个简略的 MyList,并给它加上切片性能。(PS:仅作演示,不保障其它性能的齐备性)。

class MyList():
    def __init__(self):
        self.data = []
    def append(self, item):
        self.data.append(item)
    def __getitem__(self, key):
        print("key is :" + str(key))
        return self.data[key]

l = MyList()
l.append("My")
l.append("name")
l.append("is")
l.append("Python 编程学习圈")

print(l[3])
print(l[:2])
print(l['hi'])

### 输入后果:key is : 3
Python 编程学习圈
key is : slice(None, 2, None)
['My', 'name']
key is : hi
Traceback (most recent call last):
...
TypeError: list indices must be integers or slices, not str

从输入后果来看,自定义的 MyList 既反对按索引查找,也反对切片操作,这正是咱们的目标。

特地须要阐明的是,此例中的 __getitem__() 办法会依据不同的参数类型而实现不同的性能(取索引位值或切片值),也会得当地解决异样,所以并不需要咱们再去写繁琐的解决逻辑。网上有不少学习材料齐全是在误人子弟,它们会教你辨别参数的不同类型,而后写一大段代码来实现索引查找和切片语法,几乎是画龙点睛。上面的就是一个代表性的谬误示例:

### 略去其它代码 ####
def __getitem__(self, index):
    cls = type(self)
    if isinstance(index, slice):  # 如果 index 是个切片类型,则结构新实例
        return cls(self._components[index])
    elif isinstance(index, numbers.Integral):  # 如果 index 是个数,则间接返回
        return self._components[index]
    else:
        msg = "{cls.__name__} indices must be integers"
        raise TypeError(msg.format(cls=cls))

3、自定义字典实现切片性能
切片是序列类型的个性,所以在上例中,咱们不须要写切片的具体实现逻辑。然而,对于其它非序列类型的自定义对象,就得本人实现切片逻辑。以自定义字典为例(PS:仅作演示,不保障其它性能的齐备性):

class MyDict():
    def __init__(self):
        self.data = {}
    def __len__(self):
        return len(self.data)
    def append(self, item):
        self.data[len(self)] = item
    def __getitem__(self, key):
        if isinstance(key, int):
            return self.data[key]
        if isinstance(key, slice):
            slicedkeys = list(self.data.keys())[key]
            return {k: self.data[k] for k in slicedkeys}
        else:
            raise TypeError

d = MyDict()
d.append("My")
d.append("name")
d.append("is")
d.append("Python 编程学习圈")
print(d[2])
print(d[:2])
print(d[-4:-2])
print(d['hi'])

### 输入后果:is
{0: 'My', 1: 'name'}
{0: 'My', 1: 'name'}
Traceback (most recent call last):
...
TypeError

上例的关键点在于将字典的键值取出,并对键值的列表做切片解决,其妙处在于,不必放心索引越界和正数索引,将字典切片转换成了字典键值的切片,最终实现目标。

4、小结
本文介绍了__getitem__()魔术办法,并用于实现自定义对象(以列表类型和字典类型为例)的切片性能,心愿对你有所帮忙。

以上就是本次分享的所有内容,想要理解更多 python 常识欢送返回公众号:Python 编程学习圈,发送“J”即可收费获取,每日干货分享

退出移动版