使用__getitem__同时支持索引与键值访问是否合理?
关于自定义度量单位类的索引访问合理性问题
我定义了一个带subunits的度量单位通用类(例如码、英尺、英寸的长度,或度、分、秒的角度),该类的属性既可以像tuple一样通过索引访问,也可以像dict一样通过键访问,同时还支持点标记访问属性(因重写__setattr__方法,属性不可修改)。相关代码如下:
class MeasureWithSubunits(): units = ('days', 'hours', 'minutes') # Class variable can be assigned as normal at runtime. # Assigned here as an example. def __init__(self, *args) -> None: # Tidy up the input ... for i, unit in enumerate(self.units): self.__dict__[unit] = args[i] if i < len(args) else 0 def __getitem__(self, index): if type(index) is int: return self.__dict__[self.units[index]] elif type(index) is slice: return [self.__dict__[self.units[i]] for i in range( index.start or 0, index.stop or len(self.units), index.step or 1 )] else: return self.__dict__[index] def __len__(self) -> int: return len(self.units) def __setattr__(self, name, attr_value): raise AttributeError("Does not support attribute assignment")
我的问题是:同时允许方括号以两种近乎矛盾的方式使用是否“不妥”?尤其是键值访问方式并非必要,因为已支持点标记访问。希望依据文档解答(也欢迎有理有据的观点),或者标准库中是否有类似实现?
解答
从Python设计原则看
Python之禅(PEP 20)明确提到清晰胜于晦涩、简单胜于复杂。让__getitem__同时处理整数/切片索引和字符串键,会模糊接口的语义:用户看到obj[x]时,需要额外判断x是索引还是键,增加了理解成本,不符合清晰性原则。
标准库的参考实现
标准库中没有同时混用两种__getitem__访问方式的容器类:
tuple仅支持整数/切片索引,dict仅支持可哈希的键,两者的访问边界清晰;collections.namedtuple作为结构化数据类,支持点标记访问和整数/切片索引,但不允许用键作为__getitem__的参数——若要按键访问,只能通过点标记或转换为dict后操作,这是在明确区分两种访问模式的职责。
你的场景分析
键值访问已经通过点标记obj.hours实现,再在__getitem__中支持字符串键属于冗余功能,反而降低了接口的直观性:
- 习惯
tuple用法的用户,遇到obj['hours']会产生困惑; - 点标记
obj.hours的可读性本身就优于方括号键访问obj['hours']。
另外,当前实现存在潜在歧义:如果units中包含整数形式的字符串(比如units = ('1', '2')),obj[1]会触发歧义——是取索引1对应的'2',还是取键'1'对应的值?这种极端情况会引发难以排查的bug。
建议
- 保留
__getitem__的整数/切片索引功能,对齐tuple的行为,保持序列访问的清晰性; - 移除字符串键的访问支持,引导用户使用更直观的点标记访问属性;
- 如果确实需要键式访问,可以单独提供
get方法(如obj.get('hours')),或实现__dict__转换,但不要将两种访问模式混在__getitem__中。
内容的提问来源于stack exchange,提问作者Mordechai
相关产品推荐
相关产品推荐

