Python——管理属性(1)
管理属性
这里将展开介绍前面提到的【属性拦截】技术。包含下面内容:
【1】__getattr__和__setattr__方法。把没有定义的属性获取和全部的属性赋值指向通用的处理器方法
【2】__getattribute__方法,把全部属性获取都指向一个泛型处理器
【3】property内置函数。把特定属性訪问定位到get和set处理器函数,也叫做特性(property)
【4】描写叙述符协议,把特定属性訪问定位到具有随意get和set处理器方法的类的实例
最后两种方法适用于特定属性。而前面两种方法足够通用。
本篇先介绍后面两种方法。
===============================================================================
特性property
特性协议同意我们把一个特定属性的get和set操作指向我们所提供的函数或方法,使得我们可以插入在属性訪问的时候自己主动执行的代码,拦截属性删除。
通过property内置函数来创建特性并将其分配给类属性。就像方法函数一样。一个特性管理一个单个的、特定的属性。虽然他不能广泛地捕获全部的属性訪问。但它同意我们控制訪问和赋值操作,而且同意我们自由地把一个属性从简单的数据改变为一个计算,而不会影响已有的代码。
特性和描写叙述符有非常大的关系。它们基本上是描写叙述符的一种受限制的形式。
我们能够通过把一个内置函数的结果赋给一个类属性来创建一个特性:
attrbute = property(fget, fset, fdel, doc)
这个内置函数的參数都没必要的,而且假设没有传递參数的话,全部都取默认值None,那么这种操作是不受支持的。
当使用它们的时候。我们向fget传递一个函数来拦截属性訪问。给fset传递一个函数进行赋值,而且给fdel传递一个函数进行属性删除;doc參数接收该属性的一个文档字符串,假设想要的话。
fget返回计算过的属性值,而且fset和fdel不会返回数据,返回None。
------------------------------------------------------------------------------------------------------------------------------------------
第一个样例
例如以下的类使用了一个特性来记录对一个名为name的属性的方法。实际存储的数据名为_name。以便不会和特性搞混了:
class Person:
def __init__(self,name):
self._name = name
def getName(self):
print('fetch...')
return self._name
def setName(self,value):
print('change...')
self._name = value
def delName(self):
print('remove...')
del self._name
name = property(getName,setName,delName,'name property docs') bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Robert Smith'
print(bob.name)
del bob.name print('-'*20)
sue = Person('Sue Jones')
print(sue.name)
print(Person.name.__doc__)
这个特定的特性所做的事情并不多——它仅仅是拦截并跟踪了一个属性。两个实例继承了该特性,就好像它们是附加到其类的另外两个属性一样。
然而,捕获了它们的属性訪问:
fetch...
Bob Smith
change...
fetch...
Robert Smith
remove...
--------------------
fetch...
Sue Jones
name property docs
就像全部的类属性一样,实例和较低的子类都继承特性。假设我们把样例改动为例如以下所看到的:
class Super:
def __init__(self,name):
self._name = name
def getName(self):
print('fetch...')
return self._name
def setName(self,value):
print('change...')
self._name = value
def delName(self):
print('remove...')
del self._name
name = property(getName,setName,delName,'name property docs') class Person(Super):
pass bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Robert Smith'
print(bob.name)
del bob.name print('-'*20)
sue = Person('Sue Jones')
print(sue.name)
print(Person.name.__doc__)
输出是同样的。Person子类从Super继承了name特性,而且bob实例从Person获取了它。
------------------------------------------------------------------------------------------------------------------------------------------
计算的属性
上述样例仅仅是简单了跟踪了属性訪问。然而,通常特性做的很多其它——比如。当获取属性的时候。动态地计算属性的值。看下述样例:
class PropSquare:
def __init__(self,start):
self.value = start
def getX(self):
return self.value ** 2
def setX(self,value):
self.value = value
X = property(getX,setX) # 仅仅有get和set。没有del和doc P = PropSquare(3)
P = PropSquare(32) print(P.X) # 3**2
P.X = 4
print(P.X) # 4**2
print(Q.X) # 32**2
这个样例定义了一个X属性,而且将其当做静态数据一样訪问,但实际执行的代码在获取该属性的时候计算了它的值。
------------------------------------------------------------------------------------------------------------------------------------------
使用装饰器编写特性
函数装饰器的语法是:
@decorator
def func(args):...
Python会自己主动将其翻译成对等的形式。把函数名又一次绑定到可调用的decorator的返回结果上:
def func(args):...
func = decorator(func)
因为这一映射,证实了内置函数property能够充当一个装饰器,来定义一个函数,当获取一个属性的时候自己主动执行该函数:
class Person:
@property
def name(self):...
执行的时候,装饰的方法自己主动传递给property内置函数的第一个參数(即fget)。这事实上仅仅是创建一个特性并手动绑定属性名的一种替代语法:
class Person:
def name(self):...
name = property(name)
实际上,property对象也有getter、setter和deleter方法。这些方法指定对应的特性訪问器方法而且返回特性自身的一个副本。
看下述样例:
class Person:
def __init__(self, name):
self._name = name @property
def name(self):
"name property docs"
print('fetch...')
return self._name @name.setter
def name(self,value):
print('change...')
self._name = value @name.deleter
def name(self):
print('remove...')
del self._name bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Robert Smith'
print(bob.name)
del bob.name print('-'*20)
sue = Person('Sue Jones')
print(sue.name)
print(Person.name.__doc__)
这段代码等同于上文写的第一个样例,在这个样例中,装饰仅仅是编写特性的一种替代方法。输出结果是同样的:
fetch...
Bob Smith
change...
fetch...
Robert Smith
remove...
--------------------
fetch...
Sue Jones
name property docs
和property手动赋值的结果相比,这个样例中,使用装饰器来编写特性仅仅须要3行额外的代码。所以使用装饰器更加方便。
===============================================================================
描写叙述符
描写叙述符提供了拦截属性訪问的一种替代方法,它们与特性有非常大关系。实际上,特性是描写叙述符的一种——从技术上讲,property内置函数仅仅是创建一个特定类型的描写叙述符的一种简化方式。而这样的描写叙述符在属性訪问时执行方法函数。
和特性一样,描写叙述符也管理一个单个的、特定的属性。
描写叙述符作为独立的类创建,而且针对想要拦截的属性訪问操作提供特定命名的訪问器方法——当以对应的方式訪问分配给描写叙述符类实例的属性时。描写叙述符类中的获取、设置和删除等方法自己主动执行:
class Descriptor:
"docstring goes here"
def __get__(self, instance, owener):... # Return attr value
def __set__(self, instance, value):... # Return noting(None)
def __del__(self,instance):... # Return noting(None)
带有不论什么这些方法的类都能够看做是描写叙述符。而且当它们的一个实例分配给还有一个类的属性的时候,它们的这些方法是特殊的——当訪问属性的时候,会自己主动调用它们。假设这些方法中的不论什么一个空缺,通常意味着不支持对应类型的訪问。
然而,和特性不同,省略一个__set__意味着同意这个名字在一个实例中又一次定义,因此,要使得一个属性是仅仅读的。我们必须定义__set__来捕获赋值并引发一个异常。
------------------------------------------------------------------------------------------------------------------------------------------
描写叙述符方法參数
__get__、__set__、__del__三种描写叙述符方法都传递了描写叙述符实例(self)以及描写叙述符实例所附加的客户类的实例。__get__訪问方法还额外地接收一个owner參数。指定了描写叙述符实例要附加到的类。
当中insatance參数要么是訪问的属性所属的实例(用于instance.attr),要么当所訪问的属性直接属于类的时候是None(用于class.attr)。
前者通常针对实例訪问计算一个值。假设描写叙述符对象訪问是受支持的,后者通常返回self。
比如,在以下的样例中,当获取X.attr的时候,Python自己主动执行Descriptor类的__get__方法,Subject.attr类属性分配给该方法:
>>> class Descriptor(object):
def __get__(self,instance,owner):
print(self,instance,owner,sep='\n') >>> class Subject:
attr = Descriptor() >>> X = Subject()
>>> X.attr
<__main__.Descriptor object at 0x032C0F70>
<__main__.Subject object at 0x032C0FF0>
<class '__main__.Subject'> >>> Subject.attr
<__main__.Descriptor object at 0x032C0F70>
None
<class '__main__.Subject'>
注意在第一个属性获取中自己主动传递到__get__方法中的參数,当获取X.attr的时候。就好像发生了例如以下转换:
X.attr --> Descriptor.__get__(Subject.attr, X, Subject)
当描写叙述符的实例參数为None的时候,该描写叙述符知道将直接訪问它。
------------------------------------------------------------------------------------------------------------------------------------------
仅仅读描写叙述符
例如以下所看到的,使用特性忽略set方法时,就无法对该属性赋值了。
这样能够让属性成为仅仅读的。
>>> class A :
def __init__(self,name):
self.name = name def getName(self):
return self.name
name = property(getName) >>> a = A(1)
Traceback (most recent call last):
File "<pyshell#25>", line 1, in <module>
a = A(1)
File "<pyshell#23>", line 3, in __init__
self.name = name
AttributeError: can't set attribute
可是。描写叙述符和特性不同,省略__set__方法不足以让属性称为仅仅读的,由于描写叙述符名称能够赋给一个实例。在以下的样例中,对X.a的属性赋值在实例对象X中存储了a,由此,隐藏了存储在类C中的描写叙述符:
>>> class D:
def __get__(*args):
print('get') >>> class C:
a = D() >>> X = C()
>>> X.a
get
>>> C.a
get
>>> X.a = 99
>>> X.a
99
>>> list(X.__dict__.keys())
['a']
>>> Y = C()
>>> Y.a
get
>>> C.a
get
这就是Python中全部的实例属性赋值工作的方式。而且它同意在它们的实例中类选择性地覆盖类级默认值。
要让基于描写叙述符的属性成为可读的,捕获描写叙述符类中的赋值并引发一个异常来阻止属性赋值——当要赋值的属性是一个描写叙述符的时候,Python有效地绕过了常规实例层级的赋值行为,而且把操作指向描写叙述符对象:
>>> class D:
def __get__(*args):
print('get')
def __set__(*args):
raise AttributeError('cannot set') >>> class C:
a = D() >>> X = C()
>>> X.a
get
>>> X.a = 99
Traceback (most recent call last):
File "<pyshell#35>", line 1, in <module>
X.a = 99
File "<pyshell#29>", line 5, in __set__
raise AttributeError('cannot set')
AttributeError: cannot set
------------------------------------------------------------------------------------------------------------------------------------------
第一个演示样例
如今让我们来改动前面为特性编写的第一个样例。例如以下代码定义了一个描写叙述符,来拦截对其客户类中的名为name的一个属性的訪问。
其方法使用它们的instance參数来訪问主体实例中的状态信息:
class Name:
"name descriptor docs"
def __get__(self,instance,owner):
print('fetch...')
return instance._name
def __set__(self,instance,value):
print('change...')
instance._name = value
def __delete__(self,instance):
print('remove...')
del instance._name class Person:
def __init__(self,name):
self._name = name
name = Name() bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Robert Smith'
print(bob.name)
del bob.name print('-'*20)
sue = Person('Sue Jones')
print(sue.name)
print(Name.__doc__)
执行结果例如以下,和之前的一模一样:
fetch...
Bob Smith
change...
fetch...
Robert Smith
remove...
--------------------
fetch...
Sue Jones
name descriptor docs
当描写叙述符的__get__方法执行的时候。它传递了3个对象来定义其上下文:
【1】self是Name的实例
【2】instance是Person类实例
【3】owner是Person类实例
还和特性演示样例中类似。描写叙述符实例是一个类属性。而且因此由客户类和不论什么子类的全部实例继承。
假设我们把演示样例中的Person类改动为例如以下的样子,脚本的输出是同样的:
...
class Super:
def __init__(self,name):
self._name = name
name = Name() class Person(Super):
pass
...
当然,这个样例也仅仅是追踪了属性的訪问。
------------------------------------------------------------------------------------------------------------------------------------------
计算属性
相同的,也能够用来在每次获取属性的时候计算它们的值,例如以下所看到的:
class DescSquare:
def __init__(self,start):
self.value = start
def __get__(self,instance,owner):
return self.value**2
def __set__(self,instance,value):
self.value = value class Client1:
X = DescSquare(3) class Client2:
X = DescSquare(32) c1 = Client1()
c2 = Client2() print(c1.X)
c1.X = 4
print(c1.X)
print(c2.X)
执行结果例如以下:
9
16
1024
------------------------------------------------------------------------------------------------------------------------------------------
在描写叙述符中使用状态信息
在上述两个样例中,你可能会发现它们从不同的地方获取信息——第一个样例使用存储在客户实例中的数据,第二个样例使用附加到描写叙述符对象本身的数据。实际上,描写叙述符能够使用实例状态和描写叙述符状态,或者二者的不论什么组合:
【1】描写叙述符状态用来管理内部用于描写叙述符工作的数据
【2】实例状态记录了和客户类相关的信息。以及可能由客户类创建的信息
------------------------------------------------------------------------------------------------------------------------------------------
特性和描写叙述符是怎样相关的
特性和描写叙述符有非常强的相关性——property内置函数仅仅是创建描写叙述符的一种方便方式。
既然已经知道了二者是怎样工作的,我们能够使用例如以下的一个描写叙述符类来模拟property内置函数:
class Property:
def __init__(self,fget=None,fset=None,fdel=None,doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.__doc__ = doc def __get__(self,instance,instancetype=None):
if instance is None:
return self
if self.fget is None:
raise AttributeError("can't get attribute")
return self.fget(instance) def __set__(self,instance,value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(instance,value) def __delete__(self,instance):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(instance) class Person:
def getName(self):...
def setName(self,value):...
name = Property(getName,setName)
这个Property类捕获了带有描写叙述符协议的属性訪问,而且把请求定位到创建类的时候在描写叙述符状态中传入和保存的函数或方法。
Python——管理属性(1)的更多相关文章
- Python——管理属性(2)
__getattr__和__getattribute__ 眼下已经介绍了特性property和描写叙述符来管理特定属性[參考这里],而__getattr__和__getattribute__操作符重载 ...
- python对象属性管理(2):property管理属性
使用Property管理属性 python提供了一种友好的getter.setter.deleter类方法的属性管理工具:property. property()是一个内置函数,它返回一个Proper ...
- 使用Python管理数据库
使用Python管理数据库 这篇文章的主题是如何使用Python语言管理数据库,简化日常运维中频繁的.重复度高的任务,为DBA们腾出更多时间来完成更重要的工作.文章本身只提供一种思路,写的不是很全 ...
- 使用 python 管理 mysql 开发工具箱 - 1
Mysql 是一个比较优秀的开源的数据库,很多公司都在使用.作为运维人员,经常做着一些重复性的工作,比如创建数据库实例,数据库备份等,完全都可以使用 python 编写一个工具来实现. 一.模块 Co ...
- python 类属性与方法
Python 类属性与方法 标签(空格分隔): Python Python的访问限制 Python支持面向对象,其对属性的权限控制通过属性名来实现,如果一个属性有双下划线开头(__),该属性就无法被外 ...
- python 类属性和实例属性
class AAA(): aaa = 10 # 情形1 obj1 = AAA() obj2 = AAA() print obj1.aaa, obj2.aaa, AAA.aaa # 情形2 obj1.a ...
- 【转】spring管理属性配置文件properties——使用PropertiesFactoryBean|spring管理属性配置文件properties——使用PropertyPlaceholderConfigurer
spring管理属性配置文件properties--使用PropertiesFactoryBean 对于属性配置,一般采用的是键值对的形式,如:key=value属性配置文件一般使用的是XXX.pr ...
- SpringCloud的Archaius - 动态管理属性配置
参考链接:http://www.th7.cn/Program/java/201608/919853.shtml 一.Archaius是什么? Archaius用于动态管理属性配置文件. 参考自Gett ...
- python类属性和类方法(类的结构、实例属性、静态方法)
类属性和类方法 目标 类的结构 类属性和实例属性 类方法和静态方法 01. 类的结构 1.1 术语 —— 实例 使用面相对象开发,第 1 步 是设计 类 使用 类名() 创建对象,创建对象 的动作有两 ...
随机推荐
- DP 题集 2
关于 DP 的一些题目 String painter 先区间 DP,\(dp[l][r]\) 表示把一个空串涂成 \(t[l,r]\) 这个子串的最小花费.再考虑 \(s\) 字符串,\(f[i]\) ...
- Unity 游戏开发技巧集锦之使用cookie类型的纹理模拟云层的移动
Unity 游戏开发技巧集锦之使用cookie类型的纹理模拟云层的移动 使用cookie类型的纹理模拟云层的移动 现实生活中,当阳光直射大地,而天空中又有很多云时,云层的影子总是会投射在大地上,风吹着 ...
- POJ2185 Milking Grid KMP两次(二维KMP)较难
http://poj.org/problem?id=2185 大概算是我学KMP简单题以来最废脑子的KMP题目了 , 当然细节并不是那么多 , 还是码起来很舒服的 , 题目中描写的平铺是那种瓷砖一 ...
- 课堂实验-Bag
这次的课堂实验比较简单,但尴尬的是竟然没有做出来,自己的代码能力下降了不少.IDEA的Junit测试出了问题.所以这次实验是和结对伙伴结对编程写的. public class Bag<T> ...
- python一行代码开启http
python -m SimpleHTTPServer 8000 & 监听8000端口 浏览器用127.0.0.1:8000访问 如果出现no module named SimpleHTTPSe ...
- bzoj 1143: [CTSC2008]祭祀river / 2718: [Violet 4]毕业旅行 -- 二分图匹配
1143: [CTSC2008]祭祀river Time Limit: 10 Sec Memory Limit: 162 MB Description 在遥远的东方,有一个神秘的民族,自称Y族.他们 ...
- Codeforces Beta Round #6 (Div. 2 Only) D. Lizards and Basements 2 dfs
D. Lizards and Basements 2 题目连接: http://codeforces.com/contest/6/problem/D Description This is simpl ...
- Educational Codeforces Round 10 C. Foe Pairs 水题
C. Foe Pairs 题目连接: http://www.codeforces.com/contest/652/problem/C Description You are given a permu ...
- VK Cup 2016 - Qualification Round 1 (Russian-Speaking Only, for VK Cup teams) C. Promocodes with Mistakes 水题
C. Promocodes with Mistakes 题目连接: http://www.codeforces.com/contest/637/problem/C Description During ...
- Codeforces Round #302 (Div. 2) A. Set of Strings 水题
A. Set of Strings Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/544/pr ...