首先来看一个json文件的读取。书中给出了一个json样例。该json文件有700多K,数据量充足,适合本章的例子。文件的具体内容可以在http://www.oreilly.com/pub/sc/osconfeed上查看。首先先下载数据生成json文件。

def load():url='http://www.oreilly.com/pub/sc/osconfeed'JSON="osconfeed.json"if not os.path.exists(JSON):remote=urlopen(url)with open(JSON,'wb') as local:local.write(remote.read())with open(JSON) as fp:return json.load(fp)
我们要访问json数据里面的例子,该如何访问呢,一般情况是
print feed['Schedule']['speakers'][-1]['name'] 但是这种句法有个缺点,就是很冗长。能不能按照feed.Schedule.speakers[-1].name这种比较简洁的方式来访问呢。要实现这种访问。需要对数据做下重新处理。这里要用到__getattr__方法:代码如下:
class FrozenJSON:def __init__(self,mapping):self.__data=dict(mapping)   (1)def __getattr__(self,name):if hasattr(self.__data,name):return getattr(self.__data,name)   (2)else:return FrozenJSON.build(self.__data[name])  (3)@classmethoddef build(cls,obj):if isinstance(obj,dict):      (4)return cls(obj)elif isinstance(obj,list):      (5)return [cls.build(item) for item in obj]else:                  (6)return obj
 
(1)构造一个字典,这样做确保传入的是字典
(2)确保没有此属性的时候调用__getattr__
(3)如果name是__data的属性,则返回那个属性。
(4)如果判定是字典,则返回该字典对象
(5)如果是列表,则将列表的每个元素递归的传给build方法,构建一个列表
(6)如果既不是列表也不是字典,则直接返回元素
这样实现我们就能按照前面的预期来访问元素了:raw_feed.Schedule.speakers[-1].name

使用__new__方法来创建对象

首先来介绍下__new__方法。我们通常都将__init__称为构造函数。其实在python中真正的构造函数应该是__new__。我们没有具体的去实现__new__方法。是因为从object类继承的实现已经足够了。来看一个例子:

class A(object):def __init__(self):print '__init__'def __new__(cls, *args, **kwargs):print '__new__'print clsreturn object.__new__(cls, *args, **kwargs)
if __name__=="__main__":a=A()

E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter19.py

__new__

<class '__main__.A'>

__init__

从结果可以看到首先是进入__new__,然后来生成一个对象的实例并返回。最后才是执行__init__。从这个例子可以看出在构造一个对象实例的时候,首先是进入__new__生成对象实例,然后再调用__init__方法进行初始赋值。那么我们用__new__方法来改造前面的FrozenJSON类。在前面的FrozenJSON实现中,build函数其实是不停的在递归各个字典对象,在递归过程中生成FronzenJSON实例进行处理。也就是第四步中的return cls(obj)。这里我们可以__new__来改造。class FrozenJSON1(object):def __new__(cls, args):if isinstance(args,dict):return object.__new__(cls)elif isinstance(args,list):return [cls(item) for item in arg]else:return argsdef __init__(self,mapping):self.__data=dict(mapping)def __getattr__(self,name):if hasattr(self.__data,name):return getattr(self.__data,name)else:return FrozenJSON(self.__data[name])
上面代码部分中的__new__就是实现了build方法。在__getattr__中没有找到对应name属性时候,return FrozenJSON(self.__data[name])新建一个FrozenJSON对象进行往下递归

使用特性验证属性:

首先来看一个电商应用

class LineItem(object):def __init__(self,description,weight,price):self.description=descriptionself.weight=weightself.price=pricedef subtotal(self):return self.weight*self.price

if __name__=="__main__":raisins=LineItem('Golden raisins',10,6.95)print raisins.subtotal()

目前这个实现都是正常的,客户输入要的货物数量,和单价。在这里计算出总价。但是如果客户一个不小心,把货物数量设置成了负数,后果会怎样?

if __name__=="__main__":raisins=LineItem('Golden raisins',10,6.95)print raisins.subtotal()raisins.weight=-20print raisins.subtotal()

E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter19.py

69.5

-139.0

这个时候变成了给顾客钱。是不是很囧。一般来说这种情况下都会想到将变量设置为私有变量。然后则设置值的时候进行保护。

class LineItem(object):def __init__(self,description,weight,price):self.description=descriptionself.__weight=weightself.__price=pricedef set_value(self,new_value):if new_value <=0:raise ValueError('value must be > 0')else:self.__weight=new_valuedef subtotal(self):return self.__weight*self.__price

if __name__=="__main__":raisins=LineItem('Golden raisins',10,6.95)print raisins.subtotal()raisins.set_value(0)

数量和价格都被设置成了私有变量。要想设置值必须通过set_value的方式。而在set_value的时候设置了保护,当设置的值小于等于0的时候,弹出异常。

Traceback (most recent call last):

File "E:/py_prj/fluent_python/chapter19.py", line 76, in <module>

raisins.set_value(0)

File "E:/py_prj/fluent_python/chapter19.py", line 68, in set_value

raise ValueError('value must be > 0')

ValueError: value must be > 0

对于这种情况,我们还有另外一种方法。那就是将属性变成一种特性。采用property方法。代码如下:

class LineItem(object):def __init__(self,description,weight,price):self.description=descriptionself.weight=weightself.price=pricedef subtotal(self):return self.weight*self.price@propertydef weight(self):return self.__weight@weight.setterdef weight(self,value):if value <=0:raise ValueError('value must be > 0')else:self.__weight=value

if __name__=="__main__":raisins=LineItem('Golden raisins',10,6.95)print raisins.subtotal()raisins.weight=0

通过@property将weight变成一个特性,@weight.setter来进行赋值。虽然内置的property经常用作装饰器,但它其实是个类。代码可以改写成下面的样子:

class LineItem(object):def __init__(self,description,weight,price):self.description=descriptionself.weight=weightself.price=pricedef subtotal(self):return self.weight*self.pricedef get_weight(self):return self.__weightdef set_weight(self,value):if value <= 0:raise ValueError('value must be  > 0')else:self.__weight=valueweight=property(get_weight,set_weight)

至于用哪种方法更好,这个属于见仁见智的看法。我个人觉得用装饰器的方式看起来更简洁一些。因为可以很明白的看出赋值和读值,而不用按照惯例在方法名的前面加上get和set

接下来看下属性和特性的差别:

class Class(object):data='the class data attr'@propertydef prop(self):return 'the prop value'

if __name__=="__main__":obj=Class()print vars(obj)     (1)print obj.data      (2)obj.data='bar'print vars(obj)     (3) print obj.data      (4)print Class.data    (5)
(1)    vars函数返回的是obj的__dict__函数,没有实例属性
(2)    读取obj.data实际上读取的是Class.data的值
(3)    为obj.data赋值后,创建一个实例属性。
(4)    读取obj.data,获取的是实例属性的值。实例属性会覆盖类属性data
(5)    类属性还是以前的样子,并没有被覆盖
 
下面来看下特性的例子
if __name__=="__main__":obj=Class()print Class.prop      (1)print obj.prop        (2) obj.__dict__['prop']='foo'     (3)print vars(obj)print obj.prop                  (4)Class.prop='baz'                print obj.prop                   (5)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter19.py
<property object at 0x01B4F540>
the prop value
{'prop': 'foo'}
the prop value
foo
(1)    直接从Class中读取prop特性。获取的是特性本身
(2)    读取obj.prop
(3)    通过__dict__方法来给实例增加一个属性
(4)    此时实例有2个实例属性,data和prop, 但是在调用prop的时候仍然是读取特性的方法,而不是实例属性。表明特性没有被实例属性覆盖
(5)    当类的prop特性被覆盖后,销毁该特性对象。再次读取obj.prop的时候,Class.prop不再是特性了,因此不会覆盖obj.prop。
 
总结:从这里可以看出,当读取实例属性的时候会覆盖类的属性。而在读取实例特性的时候,特性不会被实例属性覆盖,而依然是读取类的特性。除非类特性被销毁。

转载于:https://www.cnblogs.com/zhanghongfeng/p/7468881.html

流畅python学习笔记:第十九章:动态属性和特性相关推荐

  1. Python学习笔记(十九)面向对象 - 继承

    Python学习笔记(十九)面向对象 - 继承 一.继承的概念 # 继承:子类继承父类的所有方法和属性# 1. 子类 class A(object):def __init__(self):self.n ...

  2. Python学习笔记(十九)——Matplotlib入门

    目录 Matplotlib简介 导入matplotlib模块 图的参数说明 matplotlib图像组成部分介绍 matplotlib绘图步骤分析 matplotlib实现简单图像 matplotli ...

  3. 深入理解Linux网络技术内幕学习笔记第十九章:因特网协议第四版(IPv4):Linux的原理和功能

    本章主要介绍Linux支持IP的数据结构和基本活动,如入口IP包如何传递至IP接收函数,校验和如何验证,以及IP选项如何处理. 主要的IPv4数据结构: struct iphdr{ };//ip报头 ...

  4. C++ Primer 学习笔记 第十九章 特殊工具与技术

    某些应用程序对内存分配有特殊需求,如使用new将对象放置在特定的内存空间中,为实现它,应用程序需要重载new和delete. new实际执行步骤: 1.new表达式调用operator new(或op ...

  5. python数据挖掘学习笔记】十九.鸢尾花数据集可视化、线性回归、决策树花样分析

    #2018-04-05 16:57:26 April Thursday the 14 week, the 095 day SZ SSMR python数据挖掘学习笔记]十九.鸢尾花数据集可视化.线性回 ...

  6. Python学习笔记第二十九天(N维数组(ndarray))

    Python学习笔记第二十九天 N维数组(ndarray) 构建阵列 索引阵列 ndarray的内部内存布局 阵列属性 内存布局 数据类型 其他属性 阵列接口 ctypes外部功能接口 Array方法 ...

  7. [go学习笔记.第十六章.TCP编程] 3.项目-海量用户即时通讯系统-redis介入,用户登录,注册

    1.实现功能-完成用户登录 在redis手动添加测试用户,并画出示意图以及说明注意事项(后续通过程序注册用户) 如:输入用户名和密码,如果在redis中存在并正确,则登录,否则退出系统,并给出相应提示 ...

  8. JSP学习笔记(四十九):抛弃POI,使用iText生成Word文档

    POI操作excel的确很优秀,操作word的功能却不敢令人恭维.我们可以利用iText生成rtf文档,扩展名使用doc即可. 使用iText生成rtf,除了iText的包外,还需要额外的一个支持rt ...

  9. SLAM学习笔记(十九)开源3D激光SLAM总结大全——Cartographer3D,LOAM,Lego-LOAM,LIO-SAM,LVI-SAM,Livox-LOAM的原理解析及区别

    本文为我在浙江省北大信研院-智能计算中心-情感智能机器人实验室-科技委员会所做的一个分享汇报,现在我把它搬运到博客中. 由于参与分享汇报的同事有许多是做其他方向的机器人工程师(包括硬件.控制等各方面并 ...

  10. Python学习笔记(十五):类基础

    以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-24 23:59 学习笔记 1,Python中的大多 ...

最新文章

  1. MongoDB源码概述——使用日志提升单机数据可靠性
  2. mysql 自动补齐 表名,列名 方法, 重启mysql方法
  3. 使用Python的http.server实现一个简易的Web Api对外提供HanLP拼音转换服务
  4. 元计算:IT巨头的金钱收割机,核武器
  5. MVC根据CheckBox的Value值选中对应的复选框及获取选中的Value值
  6. mysql桦仔_Microsoft SQL Server 2005技术内幕:T-SQL查询笔记
  7. 读书笔记_打开量化投资的黑箱01
  8. 如何一站式打造 AIoT 人才?
  9. 使用Cobbler安装多版本操作系统
  10. 重症监护室(ICU)100260
  11. 基于MATLAB小波变换的医学图像分割的研究
  12. 连云港师范专科学校计算机老师,喜报:我校学生在2021年中国大学生计算机设计比赛江苏省赛中获一等奖...
  13. blk_update_request: I/O error,dev fd0, sector 0
  14. 二叉树寻找节点x的所有祖先
  15. NVIDIA 有奖征文活动合集
  16. 安卓开发— —仿微信界面(一)
  17. [推荐]微软推出MSE 2010 Beta中文版 穆穆-movno1
  18. [JZOJ 5804] 简单的序列
  19. 【ABC算法】人工蜂群算法原理及代码
  20. 拼音输入法-java

热门文章

  1. PP模块常用数据库表
  2. 电脑计算机桌面窄,大神讲解电脑屏幕变窄且两边是黑的鼠标点不到?
  3. NO.15——使用Appium自动化测试爬取微信朋友圈数据
  4. ubuntu 安装百度云网盘
  5. CA策略发布目录SPC
  6. pandas绘图函数
  7. Python学习 读书笔记(1)
  8. IDEAD搭建SpringBoot项目
  9. Alpha测试和Beta测试的区别
  10. 手风琴几排簧好_手风琴三排簧和四排簧的区别