Python学习之路(42)——描述器(Descriptor)
什么是描述器(有些地方也称为描述符,这里统一以描述器)?
一般来说,一个描述器是一个有“绑定行为”的对象属性(object attribute),其属性的访问控制被描述符协议方法重写。这些方法是__get__()、__set__()和__delete__(),如果一个对象中只要包含了这三种方法中至少一个,就称其为描述器。(也就是说,描述器是实现了描述符协议,即__get__、__set__和__delete__方法的对象。)
object.__get__(self, instance, owner):获取属主类的属性(类属性访问)或者该类的一个实例的属性(实例属性访问)。owner始终是属主,instance是属性访问的实例,当属性通过owner访问时,则为None。这个方法应该返回(计算后)的属性值,或者引发一个AttributeError异常。
object.__set__(self, instance, value):设置属主类的实例instance的属性为一个新值value。
object.__delete__(self, instance):删除属主类的实例instance的属性。
需要使用描述器的情况
考虑Email属性:在向该属性分配值之前,需要对邮件格式进行检验。该描述器运行通过一个正则表达式处理电子邮件,然后对格式进行检验后将它分配给一个属性。
在其他许多情况下,Python描述器协议控制对属性的访问,如保护name属性。
创建描述器
1、使用类方法创建
class Descriptor:
def __init__(self):
self._name = ''
def __get__(self, instance, owner):
print("Getting: %s" % self._name)
return self._name
def __set__(self, instance, name):
print("Setting: %s" % name)
self._name = name.title()
def __delete__(self, instance):
print("Deleting: %s" % self._name)
del self._name
class Person:
name = Descriptor()
user = Person()
user.name = 'nicolas'
print('********************')
print(user.name)
print('********************')
del user.name
##########运行结果##########
C:\Python35\python3.exe D:/Project/Python/Pro_py3/test.py
Setting: nicolas
********************
Getting: Nicolas
Nicolas
********************
Deleting: Nicolas
Process finished with exit code 0
2、使用属性类型创建描述器
property(fget = None, fset = None, fdel = None, doc = None)
fget——属性获取方法;fset——属性设置方法;fdel——属性删除方法;doc——docstring
class Person(object):
def __init__(self):
self._name = ''
def fget(self):
print("Getting: %s" % self._name)
return self._name
def fset(self, value):
print("Setting: %s" % value)
self._name = value.title()
def fdel(self):
print("Deleting: %s" % self._name)
del self._name
name = property(fget, fset, fdel, "I'm the property.")
user = Person()
user.name = 'nicolas'
print('********************')
print(user.name)
print('********************')
del user.name
##########执行结果##########
C:\Python35\python3.exe D:/Project/Python/Pro_py3/test.py
Setting: nicolas
********************
Getting: Nicolas
Nicolas
********************
Deleting: Nicolas
Process finished with exit code 0
3、使用属性装饰器创建描述器
@property、@name.setter、@name.deleter
class Person(object):
def __init__(self):
self._name = ''
@property
def name(self):
print("Getting: %s" % self._name)
return self._name
@name.setter
def name(self, value):
print("Setting: %s" % value)
self._name = value.title()
@name.deleter
def name(self):
print("Deleting: %s" % self._name)
del self._name
user = Person()
user.name = 'nicolas'
print('********************')
print(user.name)
print('********************')
del user.name
##########执行结果##########
C:\Python35\python3.exe D:/Project/Python/Pro_py3/test.py
Setting: nicolas
********************
Getting: Nicolas
Nicolas
********************
Deleting: Nicolas
Process finished with exit code 0
4、在运行时创建描述器
class Person(object):
def addProperty(self, attribute):
# create local setter and getter with a particular attribute name
getter = lambda self: self._getProperty(attribute)
setter = lambda self, value: self._setProperty(attribute, value)
# construct property attribute and add it to the class
setattr(self.__class__, attribute, property(fget=getter, \
fset=setter, \
doc="Auto-generated method"))
def _setProperty(self, attribute, value):
print("Setting: %s = %s" % (attribute, value))
setattr(self, '_' + attribute, value.title())
def _getProperty(self, attribute):
print("Getting: %s" % attribute)
return getattr(self, '_' + attribute)
if __name__ == '__main__':
user = Person()
user.addProperty('name')
user.addProperty('phone')
user.name = 'nicolas'
user.phone = '110'
print('********************')
print(user.name)
print(user.phone)
print('********************')
print(user.__dict__)
##########执行结果##########
C:\Python35\python3.exe D:/Project/Python/Pro_py3/test.py
Setting: name = nicolas
Setting: phone = 110
********************
Getting: name
Nicolas
Getting: phone
110
********************
{'_name': 'Nicolas', '_phone': '110'}
Process finished with exit code 0
这将在运行时创建name和phone属性。它们可以根据相应的名称进行访问,按照_setProperty方法中的定义,将在对象名称空间目录中存储为_name和_phone。基本上name和phone是对内部的_name和_phone属性的访问符。
结束语
Python描述器可以利用新的样式类实现强大而又灵活的属性管理。通过结合使用描述器,可以实现优雅的编程,允许创建Setters和Getters以及只读属性,它还允许根据值或类型请求进行属性验证。
浙公网安备 33010602011771号