Python glom包初探

举报
小四毛 发表于 2018/12/20 13:37:17 2018/12/20
【摘要】 学习glom,用更pythonic的方式获取json格式数据。

大家好, 我是四毛。


这一篇是关于glom的一个介绍与初步使用,后期会将里面的各种API再给大家介绍下,。

因为是边看,边练习,然后翻译,所以个人理解可能有偏差,有错误的地方,请大家指正。

首先,这个库是用来处理一些嵌套的数据的,作者也在PyCon 2018上做了个分享,老美的PyCon还是有点质量的,不像国内的,搞的什么玩意。  

视频地址:https://www.youtube.com/watch?v=bTAFl8P2DkE&t=18m07s 

1. 官方文档地址

文档地址

2. 安装方法

pip install glom

3. 正式开始

glom,官方的说法是用PYTHONIC的方式来处理内嵌的数据。对于现实世界中的数据处理更加给力,现实世界中的数据,我的理解就是AJAX越来越流行了,处理这类数据会越来越频繁。有如下特点:

  • 对于嵌套数据结构的基于路径式的访问

  • 可读,有意义的错误消息

  • 声明性数据转换,使用轻量级,Pythonic规范

  • 内置数据探索和调试功能

     3.1 原始处理嵌套数据

下面的脚本包导入

from glom import glom

下面的data就是个简单的嵌套数据,一般都可以用下面几种方法进行处理

data = {'a': {'b': {'c': 'd'}}}
data['a']['b']['c']
data.get('a').get('b').get('c')
data.get('a', {}).get('b',{}).get('c')

但是当我们的数据改变成下面的这样时:

data2 = {'a': {'b': None}}
data2['a']['b']['c']
Traceback (most recent call last):
...
TypeError: 'NoneType' object has no attribute '__getitem__'

会报错,而且由于是嵌套数据,从错误信息里我们只知道有个None值,但是到底谁是呢,是a,是b呢,反正肯定不是我们的朋友小哪吒。

    3.2 glom出场

那么glom怎么处理上面的数据呢? 如其所言,路径式:

data = {'a': {'b': {'c': 'd'}}}
print glom(data, 'a.b.c')  # d

看起来还是很优雅, 很Pythonic。

data2 = {'a': {'b': None}}
glom(data2, 'a.b.c')

错误信息如下:

glom.core.PathAccessError: could not access 'c', part 2 of Path('a', 'b', 'c'), got error: AttributeError("'NoneType' object has no attribute 'c'",)

很明显,这个错误就很直观。 难道仅仅只有这个?当然不是

     3.2.1  Going Beyond Access

上面的是原标题,我的理解是不仅仅获取数据,还有别的呢。 首先,介绍两个基本的术语

target 目标数据,可以是字典,列表,或其他任意的对象
spec  我们想要的输出格式 【specifications】, 定义你自己所需要的格式

现在让我们跟随宇航员的脚步,探索太阳系吧。

  • 获取某个行星的名字:

target = {'galaxy': {'system': {'planet': 'jupiter'}}}
# 这个格式就是需要个字段值,所以输出的就是个字段值
spec = 'galaxy.system.planet'
glom(target, spec)
# 'jupyter'
  • 现在,宇航员们想把行星的名字放进一个列表中,数据是这样:

target = {'system': {'planets': [{'name': 'earth'}, {'name': 'jupiter'}]}}
  • 通常,处理这样的话,都要写个循环,或者搞个列表解析式,那么glom怎么处理呢?

glom(target, ('system.planets', ['name']))
print glom(target, spec)
# ['earth', 'jupiter']

是不是很简单。那么现在新需求又来了,宇航员想得到下面这个数据里面的行星的卫星的数:

target = {'system': {'planets': [{'name': 'earth', 'moons': 1},
                                  {'name': 'jupiter', 'moons': 69}]}}
  • glom解决方法:

# 自定义的格式
spec = {'names': ('system.planets', ['name']),
        'moons': ('system.planets', ['moons'])}
print glom(target, spec)
# {'moons': [1, 69], 'names': ['earth', 'jupiter']}

     3.2.2  Changing Requirements

Coalesce 是glom定义的一种结构,允许我们对于spec中的子spec进行进一步的处理,你只要在子spec中将可能存在的值定义好就行了,听起来有点绕,现在来梳理一下。

  • 首先,子spec是什么?

spec = {'names': ('system.planets', ['name']),
         'moons': ('system.planets', ['moons'])}
# 以这个为例,这里面的system.planets就是个子spec
  • 然后,使用其解析数据:

target = {'system': {
    'planets': [{'name': 'earth', 'moons': 1}, {'name': 'jupiter', 'moons': 69}],
}
}
spec = {'names': (Coalesce('system.planets', 'system.dwarf_planets'), ['name']),
         'moons': (Coalesce('system.planets', 'system.dwarf_planets'), ['moons'])}

print glom(target, spec)
# {'moons': [1, 69], 'names': ['earth', 'jupiter']}
  • 接着当我们的数据变成了这个以后

target = {'system': {'dwarf_planets': [{'name': 'pluto', 'moons': 5},
                                        {'name': 'ceres', 'moons': 0}]}}
spec = {'names': (Coalesce('system.planets', 'system.dwarf_planets'), ['name']),
         'moons': (Coalesce('system.planets', 'system.dwarf_planets'), ['moons'])}
print glom(target, spec)
# {'moons': [5, 0], 'names': ['pluto', 'ceres']}

可以看到,依然可以使用相同的spec来解析不同的目标数据。 有意思的是,你可以在target里面同时写入plantes和dwarf_plants数据试试看,会返回什么数据。 【这里应该是个惰性的匹配,只要匹配到一个,后面的就不再去匹配了】

     3.2.3  True Python Native

真正的原生python 在glom里面,你可以传值给python里面的任意的函数 举例:

  • 求和

target = {'system': {'planets': [{'name': 'earth', 'moons': 1},
                                  {'name': 'jupiter', 'moons': 69}]}}

print glom(target, {'moon_count': ('system.planets', ['moons'], sum)})

# {'moon_count': 70}

原教程这里还有个案例,但是我还没有理解好,就不写出来了,大家可以点击链接自己看一下。  

4. 结论

下一节,为大家带来其中一些重要的函数。 最后,在用的过程中,一直有个疑问,数据如下:

target = {
    'data': {
        'name': 'just_test',
        'likes': [{'ball': 'basketball'},
                  {'ball': 'football'},
                  {'water': 'swim'}]
    }
}

现在,我想返回的数据格式为:

{'name': 'just_for_test', 'likes': ['basketball', 'football', 'water']}

一开始我以为可以这么用:

spec = {
    'name': ('data.name'),
    'likes': ('data.likes', ['ball', 'water'] ),
}

但是不行,这样会报错。后来用了另外的方法:

spec = {
    'name': ('data.name'),
    'likes': ('data.likes', [lambda x: x.values()[0] if 'ball' or 'water' in x.keys() else ''] ),
}

print glom(target, spec)
# {'name': 'just_test', 'likes': ['basketball', 'football', 'swim']}

这样感觉很不爽啊,还望会的同学不吝赐教啊。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。