流畅python学习笔记:第十九章:动态属性和特性
首先来看一个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学习笔记:第十九章:动态属性和特性相关推荐
- Python学习笔记(十九)面向对象 - 继承
Python学习笔记(十九)面向对象 - 继承 一.继承的概念 # 继承:子类继承父类的所有方法和属性# 1. 子类 class A(object):def __init__(self):self.n ...
- Python学习笔记(十九)——Matplotlib入门
目录 Matplotlib简介 导入matplotlib模块 图的参数说明 matplotlib图像组成部分介绍 matplotlib绘图步骤分析 matplotlib实现简单图像 matplotli ...
- 深入理解Linux网络技术内幕学习笔记第十九章:因特网协议第四版(IPv4):Linux的原理和功能
本章主要介绍Linux支持IP的数据结构和基本活动,如入口IP包如何传递至IP接收函数,校验和如何验证,以及IP选项如何处理. 主要的IPv4数据结构: struct iphdr{ };//ip报头 ...
- C++ Primer 学习笔记 第十九章 特殊工具与技术
某些应用程序对内存分配有特殊需求,如使用new将对象放置在特定的内存空间中,为实现它,应用程序需要重载new和delete. new实际执行步骤: 1.new表达式调用operator new(或op ...
- python数据挖掘学习笔记】十九.鸢尾花数据集可视化、线性回归、决策树花样分析
#2018-04-05 16:57:26 April Thursday the 14 week, the 095 day SZ SSMR python数据挖掘学习笔记]十九.鸢尾花数据集可视化.线性回 ...
- Python学习笔记第二十九天(N维数组(ndarray))
Python学习笔记第二十九天 N维数组(ndarray) 构建阵列 索引阵列 ndarray的内部内存布局 阵列属性 内存布局 数据类型 其他属性 阵列接口 ctypes外部功能接口 Array方法 ...
- [go学习笔记.第十六章.TCP编程] 3.项目-海量用户即时通讯系统-redis介入,用户登录,注册
1.实现功能-完成用户登录 在redis手动添加测试用户,并画出示意图以及说明注意事项(后续通过程序注册用户) 如:输入用户名和密码,如果在redis中存在并正确,则登录,否则退出系统,并给出相应提示 ...
- JSP学习笔记(四十九):抛弃POI,使用iText生成Word文档
POI操作excel的确很优秀,操作word的功能却不敢令人恭维.我们可以利用iText生成rtf文档,扩展名使用doc即可. 使用iText生成rtf,除了iText的包外,还需要额外的一个支持rt ...
- SLAM学习笔记(十九)开源3D激光SLAM总结大全——Cartographer3D,LOAM,Lego-LOAM,LIO-SAM,LVI-SAM,Livox-LOAM的原理解析及区别
本文为我在浙江省北大信研院-智能计算中心-情感智能机器人实验室-科技委员会所做的一个分享汇报,现在我把它搬运到博客中. 由于参与分享汇报的同事有许多是做其他方向的机器人工程师(包括硬件.控制等各方面并 ...
- Python学习笔记(十五):类基础
以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-24 23:59 学习笔记 1,Python中的大多 ...
最新文章
- MongoDB源码概述——使用日志提升单机数据可靠性
- mysql 自动补齐 表名,列名 方法, 重启mysql方法
- 使用Python的http.server实现一个简易的Web Api对外提供HanLP拼音转换服务
- 元计算:IT巨头的金钱收割机,核武器
- MVC根据CheckBox的Value值选中对应的复选框及获取选中的Value值
- mysql桦仔_Microsoft SQL Server 2005技术内幕:T-SQL查询笔记
- 读书笔记_打开量化投资的黑箱01
- 如何一站式打造 AIoT 人才?
- 使用Cobbler安装多版本操作系统
- 重症监护室(ICU)100260
- 基于MATLAB小波变换的医学图像分割的研究
- 连云港师范专科学校计算机老师,喜报:我校学生在2021年中国大学生计算机设计比赛江苏省赛中获一等奖...
- blk_update_request: I/O error,dev fd0, sector 0
- 二叉树寻找节点x的所有祖先
- NVIDIA 有奖征文活动合集
- 安卓开发— —仿微信界面(一)
- [推荐]微软推出MSE 2010 Beta中文版 穆穆-movno1
- [JZOJ 5804] 简单的序列
- 【ABC算法】人工蜂群算法原理及代码
- 拼音输入法-java