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描述器可以利用新的样式类实现强大而又灵活的属性管理。通过结合使用描述器,可以实现优雅的编程,允许创建SettersGetters以及只读属性,它还允许根据值或类型请求进行属性验证。

posted on 2018-03-22 16:54  nicolas_Z  阅读(279)  评论(0)    收藏  举报

导航