1.引言
python内置给我们提供了collections模块,在这个模块中实现了一些专有化容器,这些专有化容器针对通用内建容器:dict,list,set,tuple进行了很好的补充,方便在实际应用中使用。
你可以参考:docs.python.org/zh-cn/3.11/...
下面我通过几个示例做演示。
2.容器数据类型
2.1.命名元组
命名元组namedtuple,它是一个工厂函数,用于创建元组子类,子类字段可以有名称,该子类用于创建类元组对象。如是后:
- 可以通过字段获取属性值
- 可以通过索引获取属性值
- 可以通过迭代获取值
基本示例:
python
# 命名元组namedtuple基本示例
# 通过namedtuple创建一个命名元组类:Point
from collections import namedtuple
Point = namedtuple("Point",['x','y'])
# 通过Point实例化一个元组
p = Point(10,20)
# 访问p属性值
print(f"通过字段名称访问p的属性值:x is {p.x} and y is {p.y}")
print(f"通过原生索引方式访问p的属性值:x is {p[0]} and y is {p[1]}")
高级玩法:
python
# 命名元组namedtuple高级玩法
# 通过namedtuple创建一个命名元组类:Point
from collections import namedtuple
Point = namedtuple("Point",['x','y'])
# 定义平面坐标系的两个点
p1 = Point(10,20)
p2 = Point(15,35)
# 在平面坐标系实现两个点相加,预期p3 = (25,55)
p3 = p1 + p2
print(f"相加后的p3是:{p3}")
# 输出结果不如预期,扩展一下吧
# 定义一个2D平面坐标系
class Point2D(Point):
def __add__(self,other):
return (self.x+other.x,self.y+other.y)
# 通过扩展后的Point2D,定义平面坐标系点
p4 = Point2D(10,20)
p5 = Point2D(15,35)
# 在平面坐标系实现两个点相加,预期p6 = (25,55)
p6 = p4 + p5
print(f"相加后的p6是:{p6}")
高级示例程序中,通过重写魔术方法,实现了平面坐标系两个点相加效果。关于python中的魔术方法,稍后详细介绍。
2.2.计数器
Counter是字典dict子类,有了它,我们可以在应用中轻松实现计数器功能。
python
# 计数器Counter示例
from collections import Counter
# 定义计数器不同方式
# 定义空计数器
c1 = Counter()
c1['red'] = 1
print(f"颜色red当前计数值:{c1['red']}")
# 通过映射定义计数器
c2 = Counter({"red":1,"green":2})
print(f"颜色green当前计数值:{c2['green']}")
# 通过关键字定义计数器
c3 = Counter(blue=1,yellow=2)
print(f"颜色blue当前计数值:{c3['blue']}")
2.3.顺序字典
顺序字典是字典dict的子类,它具有专门用于重新排列字典顺序的方法 。在实际应用中,通过顺序字典可以很方便实现缓存系统。比如
最近更新缓存策略系统:
python
# 最近更新缓存系统
from collections import Counter
# 定义扩展OrderedDict,实现最后更新缓存系统
class LastUpdatedOrderedDict(OrderedDict):
'Store items in the order the keys were last added'
def __setitem__(self, key, value):
super().__setitem__(key, value)
self.move_to_end(key)
# 实例化缓存系统
myCache = LastUpdatedOrderedDict()
myCache['a'] = '小王'
print(f"加入a后的缓存系统:{myCache}")
myCache['b'] = '老王'
print(f"加入b后的缓存系统:{myCache}")
LRU更新策略缓存系统:
python
# LRU更新策略缓存系统
from collections import Counter
from time import time
class TimeBoundedLRU:
"LRU Cache that invalidates and refreshes old entries."
def __init__(self, func, maxsize=128, maxage=30):
self.cache = OrderedDict() # { args : (timestamp, result)}
self.func = func
self.maxsize = maxsize
self.maxage = maxage
def __call__(self, *args):
if args in self.cache:
self.cache.move_to_end(args)
timestamp, result = self.cache[args]
if time() - timestamp <= self.maxage:
return result
result = self.func(*args)
self.cache[args] = time(), result
if len(self.cache) > self.maxsize:
self.cache.popitem(0)
return result
2.4.子类化对象
collections模块中还提供了一系列以User开头的子类化对象,方便我们做二次扩展。比如玩一玩UserDict
python
# 扩展实现自定义字典,当向字典中添加存在的key时,
# 改变字典默认覆盖行为,通过提示用户不要覆盖
from collections import UserDict
# 定义一个类,扩展UserDict
class MyDict(UserDict):
def __setitem__(self,key,value):
if key in self.data.keys():
print(f"{key}已经在字典中存在,不需要覆盖!")
else:
self.data[str(key)] = value
# 实例化使用字典
dict1 = MyDict()
dict1['name']='小王'
dict1['age'] = 18
# name重复试试
dict1['name']='老王'
2.5.魔术方法
在以上容器数据类型示例中,我们都使用到一些一两个下划线开头,且以两个下划线结尾的方法。在python中,这类方法叫做魔术方法,为什么这么叫?就是因为通过它们我们可以像玩魔术一样玩转python。
言归正传,原生python给我们提供了两类接口:一类是基类,用于继承扩展;一类是魔术方法,用于重写。python中任何一个类,或者对象都可以通过dir方法看到它相应的魔术方法
python
# 扩展实现自定义字典,当向字典中添加存在的key时,
# 改变字典默认覆盖行为,通过提示用户不要覆盖
from collections import UserDict
# 定义一个类,扩展UserDict
class MyDict(UserDict):
def __setitem__(self,key,value):
if key in self.data.keys():
print(f"{key}已经在字典中存在,不需要覆盖!")
else:
self.data[str(key)] = value
# 实例化使用字典
dict1 = MyDict()
# 查看MyDict魔术方法
dir(MyDict)
通过重写这些魔术方法,可以改变扩展类的行为,从而完成业务需要。建议你可以多去试试!