python描述符和property
2015-09-04 18:03
447 查看
Python描述符简介
Python之美[从菜鸟到高手]–玩转描述符和属性
在说property之前,我们需要理解描述符,因为不管property还是classmethod都是构建在描述符的基础上,那么到底什么是描述符呢?
描述符,用一句话来说,就是将某种特殊类型的类的实例指派给另一个类的属性(注意:这里是类属性,而不是对象属性)。而这种特殊类型的类就是实现了get,set,delete的新式类(即继承object)。
其中Descriptor就是一个描述符类,只要实现了get等三种方法中一个或几个都是描述符类。
从输出结果我们可以看出,get方法中的object就是调用描述符对象的实例,即对象demo,type就是demo的类。你也许觉得好奇,为啥get的参数是这个,不急,看完下面你就懂了。
刚才说了,描述符类的实例必须是类属性,我们将描述符类指定为对象属性,代码如下:
你会发现,压根没有调用get方法,那么调用过程到底是怎么样的呢?
其实调用test.desc,等价于调用type(test).dict[‘desc’].get(test,type(test)),懂了吧!因为DescTest类没有’desc’属性。
调用Demo.desc,等价于调用Demo.dict[‘desc’].get(None,Descriptor),所以类也可以直接调用。
下面的内容转自第一篇博客,写的很好也很清楚
简介
Python 2.2 引进了 Python 描述符,同时还引进了一些新的样式类,但是它们并没有得到广泛使用。Python 描述符是一种创建托管属性的方法。除了其他优点外,托管属性还用于保护属性不受修改,或自动更新某个依赖属性的值。
描述符增加了对 Python 的理解,改善了编码技能。本文介绍了描述符协议,并演示了如何创建和使用描述符。
描述符协议
Python 描述符协议 只是一种在模型中引用属性时指定将要发生事件的方法。它允许编程人员轻松、有效地管理属性访问:
在其他编程语言中,描述符被称作 setter 和 getter,而公共函数用于获得 (Get) 和设置 (Set) 一个私有变量。Python 没有私有变量的概念,而描述符协议可以作为一种 Python 的方式来实现与私有变量类似的功能。
总的来说,描述符就是一个具有绑定行为的对象属性,其属性访问将由描述符协议中的方法覆盖。这些方法为 get、set 和 delete。如果这些方法中的任何一个针对某个对象定义,那么它就被认为是一个描述符。通过 清单 1 进一步了解这些方法。
其中:
get 用于访问属性。它返回属性的值,或者在所请求的属性不存在的情况下出现 AttributeError 异常。
set 将在属性分配操作中调用。不会返回任何内容。
delete 控制删除操作。不会返回内容。
需要注意,描述符被分配给一个类,而不是实例。修改此类,会覆盖或删除描述符本身,而不是触发它的代码。
需要使用描述符的情况
考虑 email 属性。在向该属性分配值之前,需要对邮件格式进行检验。该描述符允许通过一个正则表达式处理电子邮件,然后对格式进行检验后将它分配给一个属性。
在其他许多情况下,Python 协议描述符控制对属性的访问,如保护 name 属性。
创建描述符
您可以通过许多方式创建描述符:
以下示例在其操作方面均相似。不同之处在于实现方法。
使用类方法创建描述符
清单 2 演示在 Python 中控制属性分配非常简单。
通过以下方法覆盖父类的 set()、get() 和 delete() 方法,创建一个描述符类:
并在分配之前将属性值修改为标题(第一个字母大写,其他字母为小写)。这样做有助于存储和输出名称。
大写转换同样可以移动到 get() 方法。_value 有一个初始值,并根据 get 请求转换为标题。
使用属性类型创建描述符
虽然 清单 2 中定义的描述符是有效的且可以正常使用,但是还可以使用属性类型的方法。通过使用 property(),可以轻松地为任意属性创建可用的描述符。创建 property() 的语法是 property(fget=None, fset=None, fdel=None, doc=None),其中:
使用属性重写该例子,如 清单 3 所示。
显然,结果是相同的。注意,fget、fset 和 fdel 方法是可选的,但是如果没有指定这些方法,那么将在尝试各个操作时出现一个 AttributeError 异常。例如,声明 name 属性时,fset 被设置为 None,然后开发人员尝试向 name 属性分配值。这时将出现一个 AttributeError 异常。
这种方法可以用于定义系统中的只读属性。
使用属性修饰符创建描述符
可以使用 Python 修饰符创建描述符,如 清单 4 所示。Python 修饰符是对 Python 语法的特定修改,能够更方便地更改函数和方法。在本例中,将修改属性管理方法。在 developerWorks 文章 Decorators make magic easy 中寻找更多有关应用 Python 修饰符的信息。
清单 4. 使用属性修饰符创建描述符
在运行时创建描述符
前面的所有例子都使用了 name 属性。该方法的局限性在于需要对各个属性分别覆盖 set()、get() 和 delete()。清单 5 提供了一个可能的解决方案,帮助开发人员在运行时添加 property 属性。该解决方案使用属性类型构建数据描述符。
清单 5. 在运行时创建描述符
这将在运行时创建 name 和 phone 属性。它们可以根据相应的名称进行访问,但是按照 _setProperty 方法中的定义,将在对象名称空间目录中存储为 _name 和 _phone。基本上,name 和 phone 是对内部的 _name 和 _phone 属性的访问符。
当开发人员尝试添加 name property 属性时,您可能对系统中的 _name 属性存在疑问。实际上,它将用新的 property 属性覆盖现有的 _name 属性。这些代码允许控制如何在类内部处理属性。
结束语
Python 描述符可以利用新的样式类实现强大而灵活的属性管理。通过结合使用描述符,可以实现优雅的编程,允许创建 Setters 和 Getters 以及只读属性。它还允许您根据值或类型请求进行属性验证。您可以在许多不同的领域应用描述符,但是使用时需保持谨慎的态度,避免由于覆盖普通对象行为而产生不必要的代码复杂性。
Python之美[从菜鸟到高手]–玩转描述符和属性
在说property之前,我们需要理解描述符,因为不管property还是classmethod都是构建在描述符的基础上,那么到底什么是描述符呢?
描述符,用一句话来说,就是将某种特殊类型的类的实例指派给另一个类的属性(注意:这里是类属性,而不是对象属性)。而这种特殊类型的类就是实现了get,set,delete的新式类(即继承object)。
[code] class Descriptor(object): def __get__(self,object,type): print 'get',self,object,type def __set__(self,object,value): print 'set',self,object,value class Demo(object): desc= Descriptor() demo=Demo() demo.desc # get <__main__.Descriptor object at 0x0269BC90> <__main__.Demo object at 0x0269BD50> <class '__main__.Demo'> demo.desc='my descriptor' #set <__main__.Descriptor object at 0x0269BC90> <__main__.Demo object at 0x0269BD50> my descriptor
其中Descriptor就是一个描述符类,只要实现了get等三种方法中一个或几个都是描述符类。
从输出结果我们可以看出,get方法中的object就是调用描述符对象的实例,即对象demo,type就是demo的类。你也许觉得好奇,为啥get的参数是这个,不急,看完下面你就懂了。
刚才说了,描述符类的实例必须是类属性,我们将描述符类指定为对象属性,代码如下:
[code] class DescTest(object): def __init__(self): self.desc=Descriptor() test=DescTest() test.desc
你会发现,压根没有调用get方法,那么调用过程到底是怎么样的呢?
其实调用test.desc,等价于调用type(test).dict[‘desc’].get(test,type(test)),懂了吧!因为DescTest类没有’desc’属性。
调用Demo.desc,等价于调用Demo.dict[‘desc’].get(None,Descriptor),所以类也可以直接调用。
下面的内容转自第一篇博客,写的很好也很清楚
简介
Python 2.2 引进了 Python 描述符,同时还引进了一些新的样式类,但是它们并没有得到广泛使用。Python 描述符是一种创建托管属性的方法。除了其他优点外,托管属性还用于保护属性不受修改,或自动更新某个依赖属性的值。
描述符增加了对 Python 的理解,改善了编码技能。本文介绍了描述符协议,并演示了如何创建和使用描述符。
描述符协议
Python 描述符协议 只是一种在模型中引用属性时指定将要发生事件的方法。它允许编程人员轻松、有效地管理属性访问:
[code]set get delete
在其他编程语言中,描述符被称作 setter 和 getter,而公共函数用于获得 (Get) 和设置 (Set) 一个私有变量。Python 没有私有变量的概念,而描述符协议可以作为一种 Python 的方式来实现与私有变量类似的功能。
总的来说,描述符就是一个具有绑定行为的对象属性,其属性访问将由描述符协议中的方法覆盖。这些方法为 get、set 和 delete。如果这些方法中的任何一个针对某个对象定义,那么它就被认为是一个描述符。通过 清单 1 进一步了解这些方法。
[code]清单 1. 描述符方法 __get__(self, instance, owner) __set__(self, instance, value) __delete__(self, instance)
其中:
get 用于访问属性。它返回属性的值,或者在所请求的属性不存在的情况下出现 AttributeError 异常。
set 将在属性分配操作中调用。不会返回任何内容。
delete 控制删除操作。不会返回内容。
需要注意,描述符被分配给一个类,而不是实例。修改此类,会覆盖或删除描述符本身,而不是触发它的代码。
需要使用描述符的情况
考虑 email 属性。在向该属性分配值之前,需要对邮件格式进行检验。该描述符允许通过一个正则表达式处理电子邮件,然后对格式进行检验后将它分配给一个属性。
在其他许多情况下,Python 协议描述符控制对属性的访问,如保护 name 属性。
创建描述符
您可以通过许多方式创建描述符:
[code]创建一个类并覆盖任意一个描述符方法:__set__、__ get__ 和 __delete__。当需要某个描述符跨多个不同的类和属性,例如类型验证,则使用该方法。 使用属性类型,这种方法可以更加简单、灵活地创建描述符。 使用属性描述符,它结合了属性类型方法和 Python 描述符。
以下示例在其操作方面均相似。不同之处在于实现方法。
使用类方法创建描述符
清单 2 演示在 Python 中控制属性分配非常简单。
[code]清单 2. 使用类方法创建描述符 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._name class Person(object): name = Descriptor() 使用这些代码并查看输出: >>> user = Person() >>> user.name = 'john smith' Setting: john smith >>> user.name Getting: John Smith 'John Smith' >>> del user.name Deleting: John Smith
通过以下方法覆盖父类的 set()、get() 和 delete() 方法,创建一个描述符类:
[code]get 将输出 Getting delete 将输出 Deleting set 将输出 Setting
并在分配之前将属性值修改为标题(第一个字母大写,其他字母为小写)。这样做有助于存储和输出名称。
大写转换同样可以移动到 get() 方法。_value 有一个初始值,并根据 get 请求转换为标题。
使用属性类型创建描述符
虽然 清单 2 中定义的描述符是有效的且可以正常使用,但是还可以使用属性类型的方法。通过使用 property(),可以轻松地为任意属性创建可用的描述符。创建 property() 的语法是 property(fget=None, fset=None, fdel=None, doc=None),其中:
[code]fget:属性获取方法 fset:属性设置方法 fdel:属性删除方法 doc:docstring
使用属性重写该例子,如 清单 3 所示。
[code]清单 3. 使用属性类型创建描述符 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.name Getting: John Smith 'John Smith' >>> del user.name Deleting: John Smith
显然,结果是相同的。注意,fget、fset 和 fdel 方法是可选的,但是如果没有指定这些方法,那么将在尝试各个操作时出现一个 AttributeError 异常。例如,声明 name 属性时,fset 被设置为 None,然后开发人员尝试向 name 属性分配值。这时将出现一个 AttributeError 异常。
这种方法可以用于定义系统中的只读属性。
[code]name = property(fget, None, fdel, "I'm the property") user.name = 'john smith' 输出为: Traceback (most recent call last): File stdin, line 21, in mоdule user.name = 'john smith' AttributeError: can't set attribute
使用属性修饰符创建描述符
可以使用 Python 修饰符创建描述符,如 清单 4 所示。Python 修饰符是对 Python 语法的特定修改,能够更方便地更改函数和方法。在本例中,将修改属性管理方法。在 developerWorks 文章 Decorators make magic easy 中寻找更多有关应用 Python 修饰符的信息。
清单 4. 使用属性修饰符创建描述符
[code]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
在运行时创建描述符
前面的所有例子都使用了 name 属性。该方法的局限性在于需要对各个属性分别覆盖 set()、get() 和 delete()。清单 5 提供了一个可能的解决方案,帮助开发人员在运行时添加 property 属性。该解决方案使用属性类型构建数据描述符。
清单 5. 在运行时创建描述符
[code]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) 让我们运行这段代码: >>> user = Person() >>> user.addProperty('name') >>> user.addProperty('phone') >>> user.name = 'john smith' Setting: name = john smith >>> user.phone = '12345' Setting: phone = 12345 >>> user.name Getting: name 'John Smith' >>> user.__dict__ {'_phone': '12345', '_name': 'John Smith'}
这将在运行时创建 name 和 phone 属性。它们可以根据相应的名称进行访问,但是按照 _setProperty 方法中的定义,将在对象名称空间目录中存储为 _name 和 _phone。基本上,name 和 phone 是对内部的 _name 和 _phone 属性的访问符。
当开发人员尝试添加 name property 属性时,您可能对系统中的 _name 属性存在疑问。实际上,它将用新的 property 属性覆盖现有的 _name 属性。这些代码允许控制如何在类内部处理属性。
结束语
Python 描述符可以利用新的样式类实现强大而灵活的属性管理。通过结合使用描述符,可以实现优雅的编程,允许创建 Setters 和 Getters 以及只读属性。它还允许您根据值或类型请求进行属性验证。您可以在许多不同的领域应用描述符,但是使用时需保持谨慎的态度,避免由于覆盖普通对象行为而产生不必要的代码复杂性。
相关文章推荐
- SFTP基于Key多进程批量并发上传文件实现案例(python)
- python猜数游戏和天气查询
- python的测试框架(一) -- 单元测试框架
- (六)python共享代码步骤
- (五)在python中创建一个函数
- PEP8 Python 编码规范
- 【Python学习日记】 第四天
- python相关的一些站点
- python发邮件
- python的注释
- python setattr(),getattr()函数
- python调试技术之性能优化
- python调试技术之内存泄露
- python调试技术之死锁
- python cookbook:第二章 字符串和文本
- Python中的全局变量与局部变量
- python爬虫框架scrapy操作步骤
- 关于python,数据挖掘,自然语言处理的一些学习资源
- Python List Merge for game 2048 (part1)
- python中关于正则表达式