流畅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)
@classmethod
def 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 cls
return 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 args
def __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=description
self.weight=weight
self.price=price
def 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=-20
print 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=description
self.__weight=weight
self.__price=price
def set_value(self,new_value):
if new_value <=0:
raise ValueError('value must be > 0')
else:
self.__weight=new_value
def 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=description
self.weight=weight
self.price=price
def subtotal(self):
return self.weight*self.price
@property
def weight(self):
return self.__weight
@weight.setter
def 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=description
self.weight=weight
self.price=price
def subtotal(self):
return self.weight*self.price
def get_weight(self):
return self.__weight
def set_weight(self,value):
if value <= 0:
raise ValueError('value must be > 0')
else:
self.__weight=value
weight=property(get_weight,set_weight)
至于用哪种方法更好,这个属于见仁见智的看法。我个人觉得用装饰器的方式看起来更简洁一些。因为可以很明白的看出赋值和读值,而不用按照惯例在方法名的前面加上get和set
接下来看下属性和特性的差别:
class Class(object):
data='the class data attr'
@property
def 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。
总结:从这里可以看出,当读取实例属性的时候会覆盖类的属性。而在读取实例特性的时候,特性不会被实例属性覆盖,而依然是读取类的特性。除非类特性被销毁。
流畅python学习笔记:第十九章:动态属性和特性的更多相关文章
- 流畅python学习笔记:第十一章:抽象基类
__getitem__实现可迭代对象.要将一个对象变成一个可迭代的对象,通常都要实现__iter__.但是如果没有__iter__的话,实现了__getitem__也可以实现迭代.我们还是用第一章扑克 ...
- 流畅python学习笔记:第十七章:并发处理
第十七章:并发处理 本章主要讨论Python3引入的concurrent.futures模块.在python2.7中需要用pip install futures来安装.concurrent.futur ...
- python学习笔记(十九)发送邮件
在python开发项目或者做自动化测试时候,在测试完成后需要将测试结果总结后进行上报,那么我们就可以通过发送邮件来完成这项工作. 下面我们来看看python中怎么发送邮件的,python中发送邮件可以 ...
- 流畅python学习笔记:第十七章:并发处理二
本章讨论python3.2引入的concurrent.futures模块.future是中文名叫期物.期物是一种对象,表示异步执行的操作 在很多任务中,特别是处理网络I/O.需要使用并发,因为网络有很 ...
- Python学习笔记第十九周
目录: 一.路由系统URL 1.Django请求生命周期 2.创建Django project 3.配置 4.编写程序 二.视图 三.模板 四.ORM操作 内容: 一.URL 1.Django请求生命 ...
- 流畅python学习笔记第十八章:使用asyncio编写服务器
在这一章中,将使用asyncio写一个TCP服务器.这个服务器的作用是通过规范名称查找Unicode字符,来看下代码: import asyncio from charfinder import Un ...
- 流畅python学习笔记第十八章:使用asyncio包处理并发(一)
首先是线程与协程的对比.在文中作者通过一个实例分别采用线程实现和asynchio包实现来比较两者的差别.在多线程的样例中,会用到join的方法,下面来介绍下join方法的使用. 知识点一:当一个进程启 ...
- 流畅python学习笔记第十八章:使用asyncio包处理并发(二)
前面介绍了asyncio的用法.下面我们来看下如何用协程的方式来实现之前的旋转指针的方法 @asyncio.coroutine def spin(msg): write,flush=sys.stdou ...
- python学习笔记(十九)面向对象编程,类
一.面向对象编程 面向对象,是一种程序设计思想. 编程范式:编程范式就是你按照什么方式去编程,去实现一个功能.不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路,两种最重要的编程范式分 ...
- Python学习笔记(十四)
Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...
随机推荐
- kail的aircrack-ng 移到linux系统,Ubuntu等系统
系统: debian (理论上基于debian的操作系统都可以, 如ubuntu, lubuntu, 等等) # apt-get install gcc make rt3070驱动安装: # apt- ...
- jenkins IOS- ad-hoc 打包
背景 客户无大企业证书,只有开发者证书,如果进行开发分发测试只能采用两种方式 testfight ad-hoc打包 上testfight存在一定的审核时间,排除掉,最后选择打ad-hoc的包 解决 查 ...
- Haproxy的配置
1,下载Haproxy 下载Haproxy 1.6 2,安装haproxy uname -r cd /usr/local/src/haproxy-1.6.9/ make TARGET=linux31 ...
- 快速搞定selenium grid分布式
写这篇文章,似乎有点重复造轮子的嫌疑.当看了几篇相关文章后,我还是决定把半年前的半成品给完成了. 以传统的方式部署分布式Selenium Grid集群需要耗费大量时间和机器成本来准备测试环境. Sna ...
- Web Service--第一次接触web service
Web Service 首发于开源中国 1. 背景 中国移动短信网关需求,要能够发送短信.开发材料只有一个短信发送配置:包括ID,password,code,url.一个jar包还有一个老旧的html ...
- servlet之注册登录(简写)
1.注册页面 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEn ...
- vue-项目入门
初入前端的新人在碰到vue.js后,去过官网,估计粗略的看下api文档以后会以为vue的安装只是把那串js代码直接粘贴复制到文档即可,虽然这样是可以,但那在项目中并不合适. 项目中的vue引入(配制安 ...
- 网络爬虫Web开始
一.介绍 该程序主体是<Python核心编程第二版>例20.2.本篇会修改部分代码及添加了相关注释. ps:该书该例程不能直接运行,需要修改. 二.功能 网络爬虫crawl.py抓取web ...
- 微信客户端+微信公众平台+新浪云SAE+Arduino+WS100(控制LED)
第一步:准备 1.智能手机微信客户端或微信电脑版 2.注册微信公众平台 https://mp.weixin.qq.com 3.注册新浪账号 http://www.sinacloud.com 4.拥有一 ...
- Spring Boot中使用Swagger2构建API文档
程序员都很希望别人能写技术文档,自己却很不愿意写文档.因为接口数量繁多,并且充满业务细节,写文档需要花大量的时间去处理格式排版,代码修改后还需要同步修改文档,经常因为项目时间紧等原因导致文档滞后于代码 ...