为何Python原生dict无法用点(.)访问键,继承dict的类却可以?
为什么继承自dict的类能用点(.)访问“键”,而原生dict不行?
咱们先把这个问题拆明白,其实你观察到的两种行为,本质上是字典的键和类实例的属性这两个概念的区别,以及Python对内置类型和自定义子类的不同处理逻辑。
1. 原生dict:键≠实例属性
原生的dict是Python的内置核心类型,它的设计里,字典存储的键值对和实例本身的属性是完全分开的:
- 字典的键值对存在它内部的哈希表结构里,必须用
d['key']这种方式访问; - 而
d.some_attr是在访问这个dict实例的属性——也就是dict类定义的方法(比如d.keys())或者你手动给它添加的属性(不过原生dict默认不推荐这么做,甚至有隐性限制)。
所以你运行d = {'first_key':1}; d.first_key时,Python会去查找这个dict实例有没有名为first_key的属性,而不是去字典里找键,自然就会报'dict' object has no attribute 'first_key'的错误。
2. 你的DotDict子类:点操作其实是在加实例属性,不是操作字典键
你写的class DotDict(dict): pass是一个继承了dict的自定义类,它同时拥有两个身份:
- 它是dict的子类,所以具备字典的所有功能(比如
d['key'] = value); - 它也是一个用户自定义的类实例,所以和普通Python类一样,允许你自由添加实例属性。
你运行d.first_key = 1的时候,其实不是在给这个字典对象添加键,而是在给d这个DotDict实例添加了一个名为first_key的实例属性。不信你可以打印一下d,输出会是{}(空字典),而不是{'first_key':1}——这说明字典本身的键值对里根本没有这个key,你访问的只是实例的属性而已。
3. 如何让子类真正用点访问字典的键?
如果你的需求是让子类既能用点操作访问字典的键,又能把值存在字典里,那需要重写类的__getattr__和__setattr__方法,把点操作映射到字典的键值对操作上:
class DotDict(dict): def __getattr__(self, key): # 当访问不存在的属性时,去字典里找对应的键 try: return self[key] except KeyError: # 保持和属性错误一致的报错信息 raise AttributeError(f"'DotDict' object has no attribute '{key}'") from None def __setattr__(self, key, value): # 把属性赋值映射为字典的键赋值 self[key] = value
这时候再运行:
d = DotDict() d.first_key = 1 print(d) # 输出 {'first_key': 1} print(d.first_key) # 输出 1
这才是真正把点操作和字典键绑定在了一起。
内容的提问来源于stack exchange,提问作者Ryan Ghorbandoost
相关产品推荐
相关产品推荐

