目录
前言
python中,数据的属性和处理数据的方法统称属性attribute,即方法只是可调用的属性。除了这二者之外,我们还可以创建特性property,在不改变类接口的前提下,使用存取方法(即读值方法和设值方法)修改数据属性。
python使用点号访问属性时,比如obj.attr,python解释器会调用特殊的方法(如__getattr__和__setattr__)计算属性。用户自定义的类可以通过__getattr__方法实现虚拟属性,当访问不存在的属性时,即时计算属性的值。
动态创建属性是一种元编程,框架的作者经常这么做。然而在python中相关的技术十分简单,任何人都可以使用。
一、使用动态属性转换数据
接下来示例中使用JSON格式数据源


图示JSON数据源中只列出了4条记录。整个数据集是一个JSON对象,里边有一个键"Schedule",这个键对应的值也是一个映像(相当于字典),有四个键:"conferences"、"events"、"speakers"和"venues"。这4个键对应的值都是一个记录列表,示例中各个列表中只有一条记录,每条记录都是一个字典。在完整的数据集中,列表中有成百上千条记录。不过"conferences"键对应的列表中只有一条记录。这四个列表中每条记录都有一个名为"serial"的字段,这是元素在各个列表中的唯一标识符。
第一个脚本:下载JSON数据源。


- 如果需要下载,就发出提醒信息。
- 在with语句中使用两个上下文管理器,分别用于读取和保存远程文件。即把url的远程内容读取出来然后写入本地的JSON文件。
- json.load函数解析JSON文件,返回python原生对象。在这个数据源中有这几种数据类型:dict、list、str、int。
有了以上代码,就可以审查JSON数据源中的任何字段:

- feed = json.load(fp),feed是JSON的解析结果,feed本质是python的原生类型对象,即一个字典,里边嵌套着字典和列表,存储着字符串和整数。
- 列出"Schedule"键中的4个记录集合。
- 显示4个记录集合中的记录数量。
- 深入嵌套的字典和列表,获取最后一个演讲者的名字。
- 获取那位演讲者的编号。
- 每个事件都有一个'speakers'字段,列出0个或多个演讲者的编号。
使用动态属性访问JSON类数据
上边示例中feed字典要用feed['Schedule']['events'][40]['name']来获取到具体的某个字段值,这种句法很冗长。JavaScript中,可以使用feed.Schedule.events[40].name获取那个值。下面用python实现了一个FrozenJson类可达到同样效果,但是只支持读取数据,该类能递归,自动处理嵌套的字典和列表。


- 传入嵌套的字典和列表组成的raw_feed,创建一个FrozenJSON实例。
- FrozenJSON实例能使用属性表示法遍历嵌套的字典,这里我们speakers列表中的元素数量,其中的元素都是字典。
- 使用底层字典的方法,.keys(),获取字典中所有的键。
- 使用items方法,获取字典中各个元素组成的列表 [key, value],然后显示各个集合中的元素数量,这里的value是一个列表,列表中的各个元素都是字典。
- feed.Schedule.speakers仍是列表,但如果列表里的元素是映射,即speakers[-1]是映射,则会被转换成FrozenJSON对象。
- events列表中的40号元素是一个字典,现在变成了一个FrozenJSON实例。
- talk是events列表中的第40个元素,talk本质是字典,其有一个speakers键,对应值是列表。
- 读取不存在的属性会抛出KeyError异常,而不是通常抛出的AttributeError异常。
下边是FrozenJSON对象的实现:


- 使用mapping参数创建一个字典,作为FrozenJSON类的实例属性。这样做目的有二:一是确保传入的是字典或者能转成成字典的对象,二是为安全起见创建了一个副本。
- 仅当按实例属性---类属性---继承树搜寻不到指定名称的属性(数据属性和方法统称为属性)时才调用__getattr__方法。
- 如果name是FrozenJSON类的实例属性__data的属性,返回那个属性。
- 否则,从self.__data中获取name键对应的元素,返回调用FrozenJSON.build()方法得到的结果。
- 用@classmethod修饰的方法是类方法,经常用这种方法来实现一个备选构造方法,比如这里的build就是一个备选构造方法。
- 如果obj是映射,那么就构建一个FrozenJSON对象,
- 如果是MutableSequence对象,这里必然是列表,因此把列表obj中的每个元素递归地传给build方法,列表推导式构建出一个列表。
- 如果既不是字典也不是列表,那么原封不动地返回元素。
处理无效属性名
FrozenJSON类有个缺陷:没有对名称为python关键字地属性做特殊处理,比如以如下方式构建一个对象:
![]()
此时用grad.class是无法读取对应的值的,因为在python中class是保留字:

这个时候可以用getattr(grad, 'class')来读取对应的属性:
![]()
但是Frozen类的目的是为了便于用 '.' 来访问数据,因此更好的方法是检查传给FrozenJSON.__init__方法的映射中是否有键的名称是关键字,如果有,那么在键名后加上_,然后就可以通过以下方式读取了:

FrozenJSON类的__init__方法改成如下所示:


- 先导入keyword模块,然后用keyword.iskeyword()来判断某个键是不是python保留的关键字。</

最低0.47元/天 解锁文章
643

被折叠的 条评论
为什么被折叠?



