问题:实现一个类,要求行为如同namedtuple:只存在给定名称的属性,不允许动态添加实例属性。
主要知识点在于: __slots__、描述符及property、__new__的使用
代码如下:
1 """ 2 运行环境 3 python 3.7+ 4 """ 5 from collections import namedtuple, OrderedDict 6 7 #以下为要包装的对象:1个命名元组,用于存储计数,并对外传递信息 8 Counter = namedtuple("Counter", "total put OK failed recorded keys count", 9 defaults=(0, 0, 0, 0, 0, 0, 0)) 10 11 class Demo: 12 """ 13 使用__slots__,__new__及描述符实现定制类 14 只能使用Counter中的属性进行存取 15 """ 16 #引入__slots__特殊属性用于控制实例属性 17 #_dict用于存储内部数据,此处未实现保护,可用定制描述符实现保护 18 __slots__ = list(Counter._fields)+['_dict'] 19 def __new__(cls): 20 """ 21 使用__new__特殊方法用于生成类实例之前配置类模板 22 因为描述符及特性(property)都是附在类上,而不是在实例上 23 """ 24 for f in Counter._fields: 25 #动态生成描述符或特性,用于控制属性存取 26 #python内置的property就是特殊的描述符,可以用partialmethod包装后在此处使用.此处未展示 27 #自己实现描述符当然更具定制性 28 setattr(cls,f,Descriptor(f)) 29 return super().__new__(cls) 30 def __init__(self): 31 self._dict={} 32 for f in Counter._fields: 33 self._dict[f]=0 34 35 def update(self, n: Counter = None, **kargs): 36 """ 37 使用数值累加计数器 38 当Counter与键参数同时提供时,键值为准 39 """ 40 41 #描述符实现 42 class Descriptor: 43 def __init__(self,storage_name:str): 44 self.storage_name=storage_name 45 def __set__(self,instance,value): 46 print('from descriptor..') 47 instance._dict[self.storage_name]=value 48 def __get__(self,instance,owner): 49 return instance._dict[self.storage_name] 50 51 #也可以用以下特性工厂函数实现 52 def make_property(store_name:str)->property: 53 def getter(instance): 54 print('from property getter') 55 return instance._dict[store_name] 56 def setter(instance,value): 57 print('from property setter') 58 instance._dict[store_name]=value 59 return property(getter,setter)
补充说明,以上部分逻辑并未完整考虑和优化,只是对类定制及属性存取做实现演示。另外的实现方法可参考上篇: