为了更好的浏览体验,请不要在本页面禁用 Javascript 🙂
2019-07-19
Python

Python 中如何优雅地将字典转化对象

羡慕 JS 的语法糖
先看下面一段 JS 代码
let npc = { name: { "first": "san", last: "zhang" }, hp: 1000 } npc.name.first // "san" npc["name"].first // "san"
JavaScript
JS中的“字典”天然就是一个对象,所以可以通过点的方式访问对象属性。而 python 中通过键获取映射值通常需要下面这样。
a_dict["key"] a_dict.get("key")
Python
有点怀念 JS 简洁的语法了。你要知道用点访问,可以少敲 [" *2 、 ] 4 个字符,这个语法糖难道不甜嘛?
在 python 中要用点访问,必须把字典转化为对象。
字典转对象
强大的内置电池
In [1]: from types import SimpleNamespace In [2]: npc = { ...: 'name': { ...: 'first': 'san', ...: 'last': 'zhang' ...: }, ...: 'hp': 1000 ...: } In [3]: npc Out[3]: {'name': {'first': 'san', 'last': 'zhang'}, 'hp': 1000} In [4]: npc = SimpleNamespace(**npc) In [5]: npc.hp Out[5]: 1000
Python
到目前为止一切 OK,又可以点点点了。
In [6]: npc.name Out[6]: {'first': 'san', 'last': 'zhang'} In [7]: npc.name.first --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-7-7749b491d382> in <module> ----> 1 npc.name.first AttributeError: 'dict' object has no attribute 'first'
Python
很明显,对于嵌套字典 SimpleNamespace 没法自动处理,只能转换第一层字典为对象。我们需要对其进行递归处理。
处理通用的嵌套字典
嵌套字典中的值,可能是 list,可能是 dict,也可能是基础数据类型。因此在处理递归的过程中,我们需要对不同类型的值,做不同的处理。
下面推荐内置电池中的 singledispatch
In [12]: singledispatch? Signature: singledispatch(func) Docstring: Single-dispatch generic function decorator. Transforms a function into a generic function, which can have different behaviours depending upon the type of its first argument. The decorated function acts as the default implementation, and additional implementations can be registered using the register() attribute of the generic function. File: /usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/functools.py Type: function
Python
简单的说,它是一个装饰器。可以为被装饰函数第一个参数,注册不同的处理逻辑。
举个例子
In [13]: @singledispatch ...: def mytype(obj): ...: return obj In [16]: @mytype.register(list) ...: def handle_list(obj): ...: print("i am list") ...: return obj # 返回 list 之前,打印自己的类型 In [17]: mytype([1,2,3,4]) i am list Out[17]: [1, 2, 3, 4] # 因为没有注册对于字符串的处理函数,所以会直接返回 obj,即 mytype 的处理逻辑。 In [18]: mytype("a") Out[18]: 'a'
Python
下面我们来构建一个可以处理嵌套字典的 dict2obj 函数
In [22]: @singledispatch ...: def dict2obj(o): ...: return o ...: In [23]: @dict2obj.register(dict) ...: def handle_obj(obj): ...: return SimpleNamespace(**{ k:dict2obj(v) for k,v in obj.items() }) ...: In [24]: @dict2obj.register(list) ...: def handle_list(lst): ...: return [ dict2obj(i) for i in lst]
Python
测试一下
In [28]: zhangsan = {'name': {'first': 'san', 'last': 'zhang'}, 'hp': 1000} In [29]: lisi = {'name': {'first': 'si', 'last': 'li'}, 'hp': 1000, 'friends': [zhangsan]} In [32]: lisi Out[32]: {'name': {'first': 'si', 'last': 'li'}, 'hp': 1000, 'friends': [{'name': {'first': 'san', 'last': 'zhang'}, 'hp': 1000}]} In [33]: obj_lisi = dict2obj(lisi) In [34]: obj_lisi.name Out[34]: namespace(first='si', last='li') In [35]: obj_lisi.name.first Out[35]: 'si' In [36]: obj_lisi.hp Out[36]: 1000 In [37]: obj_lisi.friends[0].name Out[37]: namespace(first='san', last='zhang') In [38]: obj_lisi.friends[0].name.first Out[38]: 'san'
Python
完整演示代码
from types import SimpleNamespace
npc = {
'name': {
'first': 'san',
'last': 'zhang'
},
'hp': 1000
}
npc
npc = SimpleNamespace(**npc)
npc.hp
npc.name
npc.name.first
from functools import singledispatch
@singledispatch
def wrap_namespace(ob):
return ob
wrap_namespace(1)
@singledispatch
def mytype(obj):
return obj
@mytype.register(list)
def handle_list(obj):
print("i am list")
return obj
mytype([1,2,3,4])
mytype("a")
def dict2obj
@singledispatch
def dict2obj(o):
return o
@dict2obj.register(dict)
def handle_obj(obj):
return SimpleNamespace(**{ k:dict2obj(v) for k,v in obj.items() })
@dict2obj.register(list)
def handle_list(lst):
return [ dict2obj(i) for i in lst]
npc
dict2obj(npc)
zhangsan = {}
zhangsan = {'name': {'first': 'san', 'last': 'zhang'}, 'hp': 1000}
lisi = {'name': {'first': 'si', 'last': 'li'}, 'hp': 1000, 'friends': [zhangsan]}
lisi
zhangsan
lisi
obj_lisi = dict2obj(lisi)
obj_lisi.name
obj_lisi.name.first
obj_lisi.hp
obj_lisi.friends[0].name
obj_lisi.friends[0].name.first
view raw dict2obj.py hosted with ❤ by GitHub
Build with gatsby,react,material-ui and Copyright 2019 Mayne Powered by gine-blog