关于常见的内置类型可参见我的上一篇文章:python数据结构(一) 内置类型.
1. 扩展内置类型
python内置类型可以通过实例进行扩展。当想要向一个内置容器对象中添加某种功能时,通常来说,我们有两种选择:1)创建一个新对象,并将某个容器作为其属性,即组合的方式,当我们想要用容器存储一些对象并利用它的一些特性时,组合是比较好的选择;2)创建内置对象的子类并添加类方法来实现需要的功能,即继承关系,如果需要修改容器对象实际的运作方式,继承的方式更合适。
1) 查看内置类型的方法
如果想要改写某个内置类型的方法,就需要用到dir()来查看,比如想要查看list自定义的方法:
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
'__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append',
'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> help(list.__contains__)
Help on wrapper_descriptor:
__contains__(self, key, /)
Return key in self.
其中,__add__就是‘+’, 这个符号是这一个语法糖。如果我们在我们自己定义的对象中重新实现了__add__方法,那么也可以使用 '+' 来连接。
__contains__的语法糖是'in'。
__setitem__和__getitem__分别实现了利用'[]'修改和获取列表的元素值
__iter__是迭代器,实现了for... in。
__new__是构造新对象的方法,每创建一个新对象的时候,都会首先调用__new__返回一个新对象,然后调用__init__对该对象的变量进行初始化。
这些特殊方法都可以通过help函数来获取它们的使用方法。
当我们在对象的方法中利用三个反引号来包装一部分文本时,help函数就可以输出这部分文本。
2) 案例
python内置的dict是无序的,collections中提供了OrderedDict使得key按照添加顺序排列。下面的例子展示了如何实现一个简单地OrderedDict:
from collections import KeysView, ItemsView, ValuesView
class DictSorted(dict):
def __new__(*args, **kwargs):
new_dict = dict.__new__(*args, **kwargs)
new_dict.ordered_keys = []
return new_dict
def __setitem__(self, key, value):
'''self[key] = value syntax'''
if key not in self.ordered_keys:
self.ordered_keys.append(key)
super().__setitem__(key, value)
def setdefault(self, key, value):
if key not in self.ordered_keys:
self.ordered_keys.append(key)
return super().setdefault(key, value)
def keys(self):
return KeysView(self)
def values(self):
return ValuesView(self)
def items(self):
return ItemsView(self)
def __iter__(self):
'''for x in self syntax'''
return self.ordered_keys.__iter__()
__new__方法创建一个新的dict,并添加一个空的列表属性。
keys, values, items返回了dict的视图,通过KeysView, ItemsView, ValuesView得到只读的view对象,它们用__iter__方法遍历所有的key,然后用__getitem__来取值,需要定义好__iter__方法来确保它们正常工作。如果不重写这三个方法,就不能返回有序的视图。
__iter__方法确保我们遍历所有的key,按照正确的顺序返回,实现了for ... in。这里返回的是order_keys中的迭代器。
这里我们只是改写了部分方法,无法确保在所有情况下这个类都可以正常使用。要实现这一功能,可以参考collections。
2. 队列
python提供了3种队列数据结构,包括:先进先出队列(FIFO),后进先出队列(LIFO,即栈),优先级队列。最重要的两个方法分别是put()和get(),用于添加元素及获取元素。
1)先进先出队列(FIFO)
每次往队尾添加元素,从队头获取元素。如果队列已满,将block设为False,则会报出异常,否则阻塞直到可以继续添加元素。get方法同理。
>>> from queue import Queue
>>> q = Queue(maxsize=3)
>>> q.get(block=False)
---------------------------------------------------------------------------
Empty Traceback (most recent call last)
<ipython-input-10-6209e13c9d73> in <module>()
----> 1 q.get(block=False)
C:\ProgramData\Anaconda3\lib\queue.py in get(self, block, timeout)
165 if not block:
166 if not self._qsize():
--> 167 raise Empty
168 elif timeout is None:
169 while not self._qsize():
Empty:
>>> q.put(4)
>>> q.put(5)
>>> q.get()
4
>>> q.get()
5
>>> q.empty()
True
2) 后进先出队列(LIFO)
后进先出队列其实就是栈,由queue.LifoQueue实现,其操作方式与Queue类似,只是进出顺序不一样。如果没有其他要求,只用list其实就可以满足栈的要求了。必须使用LIFO的原因有两个:
- LifoQueue支持多线程并发
- LifoQueue强制使用栈接口,不能在中间插入元素。
3) 优先级队列
优先级队列每次返回当前队列中最重要的元素,queue.PriorityQueue, heapq.heap都可以用于实现这一功能。如果队列为空,则get()方法将会阻塞。
一般每次添加的元素为tuple,第一个元素元素代表其优先级,第二个为数据。也可以实现__lt__方法来达到这一目的。
网友评论