描述符

来源:互联网 发布:JavaScript字符串分割 编辑:程序博客网 时间:2024/05/16 12:04

1. 定义

描述符是一个具有绑定行为的对象属性,其属性的访问被描述符协议方法覆写。这些方法是__get__()__set__()__delete__(),一个对象中只要包含了这三个方法中的至少一个就称它为描述符。

2. 使用场景

常见的使用场景是:假设你有一个对象属性叫money,你想在某些方面限制它的取值,比如限制它为int类型,并且是非负数。你应该怎么做?

你很可能想着在类的__init__()方法里面对money添加这两个限制。然而这么做并不正确,因为它只是限制了money的初始化,当你重新赋值时,这种限制并不会被执行。

有了描述符那情况就不一样了,你先将money绑定到协议方法,然后在__set()__()方法里面添加这两个限制。这样在你每次赋值给money时(不管是初始化还是修改它),都会执行限制。

3. 描述符协议

Python 描述符协议 只是一种在模型中引用属性时指定将要发生事件的方法。它允许编程人员轻松、有效地管理属性访问:

__get__(self, instance, owner)__set__(self, instance, value)__delete__(self, instance)
  • __get__ 用于访问属性。它返回属性的值,或者在所请求的属性不存在的情况下出现 AttributeError 异常。
  • __set__ 将在属性分配操作中调用。不会返回任何内容。
  • __delete__ 控制删除操作。不会返回内容。

需要注意,描述符被分配给一个类,而不是实例。修改此类,会覆盖或删除描述符本身,而不是触发它的代码。

4. 创建描述符

您可以通过许多方式创建描述符:

  • 创建一个类并覆盖任意一个描述符方法:__set____ get____delete__。当需要某个描述符跨多个不同的类和属性,例如类型验证,则使用该方法。
  • 使用属性类型,这种方法可以更加简单、灵活地创建描述符。
  • 使用属性描述符,它结合了属性类型方法和 Python 描述符。

(1)使用类方法创建描述符

使用类方法创建描述符:

class Descriptor(object):    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._nameclass Person(object):    name = Descriptor()

使用这些代码并查看输出:

>>> user = Person()>>> user.name = 'john smith'Setting: john smith>>> user.nameGetting: John Smith'John Smith'>>> del user.nameDeleting: John Smith

(2)使用属性类型创建描述符

通过使用 property(),可以轻松地为任意属性创建可用的描述符。创建 property() 的语法是 property(fget=None, fset=None, fdel=None, doc=None),其中:

  • get:属性获取方法
  • 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 = 'john smith'Setting: john smith>>> user.nameGetting: John Smith'John Smith'>>> del user.nameDeleting: John Smith

注意,fgetfsetfdel 方法是可选的,但是如果没有指定这些方法,那么将在尝试各个操作时出现一个 AttributeError 异常。例如,声明 name 属性时,fset 被设置为 None,然后开发人员尝试向 name 属性分配值。这时将出现一个 AttributeError 异常。

这种方法可以用于定义系统中的只读属性。

name = property(fget, None, fdel, "I'm the property")user.name = 'john smith'

输出为:

Traceback (most recent call last):File stdin, line 21, in mоduleuser.name = 'john smith'AttributeError: can't set attribute

(3)使用属性修饰符创建描述符

使用属性修饰符创建描述符

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


Ref:
https://www.ibm.com/developerworks/cn/opensource/os-pythondescriptors/
https://segmentfault.com/a/1190000006660339

0 0
原创粉丝点击