如何访问名称中包含点号的Python对象属性
问题背景
我现在用Bunch类把字典转换成对象,类的代码是这样的:
class Bunch(object): """ Transform a dict to an object """ def __init__(self, kwargs): self.__dict__.update(kwargs)
但我的字典里有个键是带点号的:{'test.this': True}。当我执行spam = Bunch({'test.this':True})后,用dir(spam)能看到对象有'test.this'这个属性,但没法用print(spam.test.this)访问它,请问该怎么解决?
解决方案
这确实是个常见的小坑——Python的属性名规则里不允许直接包含点号,所以spam.test.this会被解析成先获取spam.test属性,再尝试获取该属性的this子属性,而不是直接匹配'test.this'这个完整的属性名。给你几个不同场景下的解决办法:
1. 直接用字典式访问(无需修改Bunch类)
如果只是临时需要访问这个属性,最简单的方式是绕开点号语法,直接用__dict__或者getattr()来定位完整的属性名:
# 方式一:通过对象的__dict__字典直接取值 print(spam.__dict__['test.this']) # 方式二:用getattr()传入完整的属性名字符串 print(getattr(spam, 'test.this'))
这种方法不需要改动原有的Bunch类,适合偶尔处理这类属性的场景。
2. 修改Bunch类,支持点号分割的属性访问
如果希望能像spam.test.this这样自然地访问,你可以重写Bunch类的__getattr__方法,让它自动处理带点号的属性名:
class Bunch(object): """ Transform a dict to an object, supporting dot-split key access """ def __init__(self, kwargs): self.__dict__.update(kwargs) def __getattr__(self, name): # 先检查是否存在完整匹配的属性 if name in self.__dict__: return self.__dict__[name] # 尝试按点号分割属性名,递归查找 parts = name.split('.', 1) if len(parts) == 2: first_part, rest_part = parts # 优先检查是否有嵌套的Bunch对象 if first_part in self.__dict__ and isinstance(self.__dict__[first_part], Bunch): return getattr(self.__dict__[first_part], rest_part) # 否则尝试拼接成带点号的完整键名 full_key = f"{first_part}.{rest_part}" if full_key in self.__dict__: return self.__dict__[full_key] # 都找不到的话抛出标准的AttributeError raise AttributeError(f"'Bunch' object has no attribute '{name}'")
注意:如果你的字典里同时存在test(作为嵌套Bunch对象)和test.this这样的键,这个逻辑会优先匹配嵌套对象,需要根据你的实际数据结构调整优先级。
3. 预处理字典,将带点号的键转为嵌套Bunch对象
另一种更彻底的思路是在Bunch初始化时,自动把带点号的键拆分成嵌套的对象结构,比如把{'test.this': True}转换成{'test': Bunch({'this': True})},这样就能自然用点号语法访问了:
class Bunch(object): """ Transform a dict to an object, with nested support for dot-separated keys """ def __init__(self, kwargs): for key, value in kwargs.items(): if '.' in key: # 拆分键为第一层和剩余部分 first_key, rest_key = key.split('.', 1) # 初始化或获取对应的嵌套Bunch对象 if first_key not in self.__dict__ or not isinstance(self.__dict__[first_key], Bunch): self.__dict__[first_key] = Bunch({}) # 递归设置剩余的键值对 setattr(self.__dict__[first_key], rest_key, value) else: self.__dict__[key] = value
这样处理后,spam = Bunch({'test.this': True})会自动生成嵌套结构,直接执行print(spam.test.this)就能得到True了。
内容的提问来源于stack exchange,提问作者matt.LLVW

