PyYAML 解析成任意对象
我有一个Python 2.6的程序和一个YAML定义(使用PyYAML):
import yaml
x = yaml.load(
"""
product:
name : 'Product X'
sku : 123
features :
- size : '10x30cm'
weight : '10kg'
"""
)
print type(x)
print x
运行后会得到以下输出:
<type 'dict'>
{'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}
我可以用x
中的字段创建一个对象吗?
我希望能做到以下几点:
print x.features[0].size
我知道可以从已有的类创建一个实例,但这不是我在这个特定场景下想要的。
编辑:
- 更新了关于“强类型对象”的混淆部分。
- 根据Alex Martelli的建议,将对
features
的访问改为索引器。
1 个回答
你有一个字典,里面的键是字符串,值可以是数字、嵌套的字典或者列表。你想把这个字典包装成一个实例,这样就可以用属性访问的方式来代替字典的索引,用“用索引调用”的方式来代替列表的索引。至于“强类型”跟这个有什么关系,或者为什么你觉得 .features(0)
比 .features[0]
更好(用列表索引的方式更自然啊!),我不太明白,但这确实是可行的。比如,可以用一个简单的方法:
def wrap(datum):
# don't wrap strings
if isinstance(datum, basestring):
return datum
# don't wrap numbers, either
try: return datum + 0
except TypeError: pass
return Fourie(datum)
class Fourie(object):
def __init__(self, data):
self._data = data
def __getattr__(self, n):
return wrap(self._data[n])
def __call__(self, n):
return wrap(self._data[n])
所以 x = wrap(x['product'])
应该能满足你的需求(我不知道你为什么想跳过那一层,因为你的整体逻辑显然需要 x.product.features(0).size
,但显然这种跳过在调用时更合适,而不是在我刚刚展示的包装类或包装工厂函数中硬编码)。
编辑: 正如提问者所说,他确实想要 features[0]
而不是 features(0)
,只需将最后两行改为:
def __getitem__(self, n):
return wrap(self._data[n])
也就是说,定义 __getitem__
(这个魔法方法用于索引)而不是 __call__
(这个魔法方法用于实例调用)。
如果不使用“现有类”(这里是 Fourie
),可以根据被包装的字典动态创建一个新类——这也是可行的,但这就有点复杂了,甚至可以说是“黑魔法”,而且我想不出有什么实际的好处。
如果提问者能明确说明他为什么想要动态创建类,他认为这样能得到什么好处等等,我会展示怎么做(而且,可能我还会告诉他,想要的好处其实并不存在;-)。不过,简单性在任何编程工作中都是很重要的,使用“深奥的魔法”而不是像上面那样简单明了的代码,通常不是个好主意!-)