Python动态属性和特性(二)
内置的property经常用作装饰器,但它其实是一个类。在Python中,函数和类通常可以互换,因为二者都是可调用对象,而且没有实例化的new运算符,所以调用构造方法和调用工厂函数没有区别,只要能返回新的可调用对象,代替被装饰的函数,二者都可用作装饰器
property初始化方法(__init__)的完整签名如下:
property(fget=None, fset=None, fdel=None, doc=None)
所有的参数都是可选的,如果没有把函数传给某个参数,那么得到的特性对象就不允许执行相应的操作
class LineItem: 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): # <1>
return self.__weight def set_weight(self, value): # <2>
if value > 0:
self.__weight = value
else:
raise ValueError('value must be > 0') weight = property(get_weight, set_weight) # <3>
- 普通的读值方法
- 普通的设值方法
- 构建property对象,然后赋值给公开的类属性
测试LineItem这个类
line = LineItem("Golden raisins", 10, 6.5)
print("<1>:", line.subtotal()) # <1>
line.weight = -9
运行结果:
<1>: 65.0
Traceback (most recent call last):
……
ValueError: value must be > 0
当然,我们可以写成另外一种形式,也是可以通过方法来读取和设置公开属性的
class LineItem: 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:
self.__weight = value
else:
raise ValueError('value must be > 0')
特性都是类属性,但是特性管理其实是实例属性的存取,如果实例和所属的类有同名的属性,那么在获取实例读取属性时,实例属性会覆盖类属性
class Class:
data = "the class data attr" @property
def prop(self):
return "the prop value" obj = Class()
print("<1>:", vars(obj)) # <1>
print("<2>:", obj.data) # <2>
obj.data = "bar" # <3>
print("<4>:", vars(obj)) # <4>
print("<5>:", obj.data) # <5>
print("<6>:", Class.data) # <6>
运行结果:
<1>: {}
<2>: the class data attr
<4>: {'data': 'bar'}
<5>: bar
<6>: the class data attr
- vars()返回obj.__dict__属性,表明实例没有属性
- 读取obj.data,获取的是Class.data的值
- 为obj.data赋值,为实例创建一个属性
- 再次用vars()查看实例的属性
- 现在读取obj.data,实例属性覆盖类属性
- 打印类属性Class.data
下面,尝试覆盖obj的prop特性
print("<1>:", Class.prop) # <1>
print("<2>:", obj.prop) # <2>
obj.prop = "hello world"
运行结果:
<1>: <property object at 0x00000000004805E8>
<2>: the prop value
Traceback (most recent call last):
obj.prop = "hello world"
AttributeError: can't set attribute
- 直接从Class读取prop特性,获取的是特性对象本身,不会运行特性的读值方法
- 读取obj.prop会执行特性的读值方法
- 尝试设置pro实例属性,结果失败
obj.__dict__["prop"] = "hello world" # <1>
print("<2>:", vars(obj)) # <2>
print("<3>:", obj.prop) # <3>
Class.prop = "baz" # <4>
print("<5>:", obj.prop) # <5>
运行结果:
<2>: {'prop': 'hello world'}
<3>: the prop value
<5>: hello world
- 上一个例子,通过设置obj.prop会抛出异常,于是我们尝试通过obj.__dict__设置实例属性
- 可以看到上一个步骤设置成功
- 然而,读取prop时仍会运行特性的读值方法,特性没被实例属性覆盖
- 覆盖Class.prop特性,销毁特性对象
- 现在,obj.prop读取的是实例属性。Class.prop不是特性了,因此不会再覆盖obj.prop
定义一个特性工厂函数
我们将定义一个名为quantity的特性函数,替我们管理之前LineItem这个类中的weight和price这两个属性,要求这两个属性都必须大于0
def quantity(storage_name):
def qty_getter(instance): # <3>
return instance.__dict__[storage_name] def qty_setter(instance, value): # <4>
if value > 0:
instance.__dict__[storage_name] = value
else:
raise ValueError('value must be > 0') return property(qty_getter, qty_setter) class LineItem:
weight = quantity('weight') # <1>
price = quantity('price') def __init__(self, description, weight, price):
self.description = description
self.weight = weight # <2>
self.price = price def subtotal(self):
return self.weight * self.price
- 使用工厂函数把weight和price定义为类属性
- 这里,特性已经激活,确保不能把weight设置为负数或零
- qty_getter函数的第一个参数可以命名为self,但是这样做很奇怪,因为qty_getter函数不在类的定义体中,instance指代LineItem实例,在qty_getter中,从LineItem实例的__dict__获取想要的属性,然后返回
- 定义qty_setter函数,第一个参数instance也是LineItem实例,然后将值存入instance.__dict__中,这也是为了跳过特性,避免递归循环最后栈溢出
测试工厂函数
nutmeg = LineItem("Moluccan nutmeg", 8, 13.95)
print("<1>:", nutmeg.weight, nutmeg.price) # <1>
print("<2>:", sorted(vars(nutmeg).items())) # <2>
运行结果:
<1>: 8 13.95
<2>: [('description', 'Moluccan nutmeg'), ('price', 13.95), ('weight', 8)]
定义特性时,可以使用@attr.deleter装饰器包装一个方法,负责删除特性管理的属性
class BlackKnight: def __init__(self):
self.members = ['an arm', 'another arm',
'a leg', 'another leg']
self.phrases = ["'Tis but a scratch.",
"It's just a flesh wound.",
"I'm invincible!",
"All right, we'll call it a draw."] @property
def member(self):
print('next member is:')
return self.members[0] @member.deleter
def member(self):
text = 'BLACK KNIGHT (loses {})\n-- {}'
print(text.format(self.members.pop(0), self.phrases.pop(0)))
测试删除特性
b = BlackKnight()
del b.member
del b.member
del b.member
del b.member
运行结果:
BLACK KNIGHT (loses an arm)
-- 'Tis but a scratch.
BLACK KNIGHT (loses another arm)
-- It's just a flesh wound.
BLACK KNIGHT (loses a leg)
-- I'm invincible!
BLACK KNIGHT (loses another leg)
-- All right, we'll call it a draw.
Python动态属性和特性(二)的更多相关文章
- Python动态属性和特性(一)
在Python中,数据的属性和处理数据的方法统称为属性.其实,方式只是可调用的属性.除了这二者之外,我们还可以创建特性(property),在不改变类接口的前提下,使用存取方法(即读取值和设置值方法) ...
- Python 基础 面向对象之二 三大特性
Python 基础 面向对象之二 三大特性 上一篇主要介绍了Python中,面向对象的类和对象的定义及实例的简单应用,本篇继续接着上篇来谈,在这一篇中我们重点要谈及的内容有:Python 类的成员.成 ...
- Python的高级特性7:闭包和装饰器
本节跟第三节关系密切,最好放在一起来看:python的高级特性3:神奇的__call__与返回函数 一.闭包:闭包不好解释,只能先看下面这个例子: In [23]: def outer(part1): ...
- Python基础学习(二)
前一段时间学习了Python数据类型,语句和函数,目前书写python的新特性,继续练手!!!! 一.切片 之前我们从python的list 或者 tuple中取得元素都是这样写,显然不够灵活 lis ...
- python的高级特性:切片,迭代,列表生成式,生成器,迭代器
python的高级特性:切片,迭代,列表生成式,生成器,迭代器 #演示切片 k="abcdefghijklmnopqrstuvwxyz" #取前5个元素 k[0:5] k[:5] ...
- 进击的Python【第十二章】:mysql介绍与简单操作,sqlachemy介绍与简单应用
进击的Python[第十二章]:mysql介绍与简单操作,sqlachemy介绍与简单应用 一.数据库介绍 什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,每个数 ...
- python基础篇(二)
PYTHON基础篇(二) if:else,缩进 A:if的基础格式和缩进 B:循环判断 C:range()函数和len()函数 D:break,contiue和pass语句 for,while循环 函 ...
- python异步IO编程(二)
python异步IO编程(二) 目录 开门见山 Async IO设计模式 事件循环 asyncio 中的其他顶层函数 开门见山 下面我们用两个简单的例子来让你对异步IO有所了解 import asyn ...
- Python的单元测试(二)
title: Python的单元测试(二) date: 2015-03-04 19:08:20 categories: Python tags: [Python,单元测试] --- 在Python的单 ...
随机推荐
- java es 骤合操作
ElasticSearch java API - 聚合查询 以球员信息为例,player索引的player type包含5个字段,姓名,年龄,薪水,球队,场上位置.index的mapping为: &q ...
- ajax在购物车中的应用
代码如下: 购物车页面: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http: ...
- android studio项目提交Git@OSC
转载地址:http://www.bubuko.com/infodetail-977061.html 先到git.oscchina.net网站上申请个账号,然后创建一个项目.过程不再说了. 新建工程后, ...
- postgres创建库时指定编码格式
postgres新建数据库时如果没有指定编码格式,会用默认的编码格式: 对于已经存在的数据库,虽然可以用:set client_encoding to 'UTF8'; set server_encod ...
- CentOS7.3+MySQL5.7+Apache2.4+PHP7.1+phpMyAdmin4.7+JDK1.8+SVN1.6+Jenkins2.1环境搭建
CentOS7.3+MySQL5.7+Apache2.4+PHP7.1+phpMyAdmin4.7+JDK1.8+SVN1.6+Jenkins2.1环境搭建 1.安装CentOS7.3虚拟机安装说明: ...
- T-SQL查询高级—SQL Server索引中的碎片和填充因子
写在前面:本篇文章需要你对索引和SQL中数据的存储方式有一定了解.标题中高级两个字仅仅是因为本篇文章需要我的T-SQL进阶系列文章的一些内容作为基础. 简介 在SQL Server中,存储数据 ...
- 【转】iOS开发-文件管理(一)
iOS开发-文件管理(一) 一.iOS中的沙盒机制 iOS应用程序只能对自己创建的文件系统读取文件,这个独立.封闭.安全的空间,叫做沙盒.它一般存放着程序包文件(可执行文件).图片.音频.视频.pli ...
- MySql查询时间段的方法
本文实例讲述了MySql查询时间段的方法.分享给大家供大家参考.具体方法如下: MySql查询时间段的方法未必人人都会,下面为您介绍两种MySql查询时间段的方法,供大家参考. MySql的时间字段有 ...
- BCB:WebBrowser 控件说明
控件文件:system32\shdocvw.oca shdocvw.dll 注册:regsvr32 shdocvw.dll WebBrowser 是 IE 内核做的 VB 控件, WebBrow ...
- “CTL_CODE”未定义
C4013 “CTL_CODE”未定义:假设外部返回 int 要加入 #include<winioctl.h> 并且要放在#include<windows.h>的后面