Python黑魔法@property装饰器的使用技巧解析

@property有什么用呢?表面看来,就是将一个方法用属性的方式来访问.
上代码,代码最清晰了.

class Circle(object):
  def __init__(self, radius):
    self.radius = radius 

  @property
  def area(self):
    return 3.14 * self.radius ** 2 

c = Circle(4)
print c.radius
print c.area

可以看到,area虽然是定义成一个方法的形式,但是加上@property后,可以直接c.area,当成属性访问.
现在问题来了,(不是挖掘机技术哪家强),每次调用c.area,都会计算一次,太浪费cpu了,怎样才能只计算一次呢?这就是lazy property.

class lazy(object):
  def __init__(self, func):
    self.func = func 

  def __get__(self, instance, cls):
    val = self.func(instance)
    setattr(instance, self.func.__name__, val)
    return val 

class Circle(object):
  def __init__(self, radius):
    self.radius = radius 

  @lazy
  def area(self):
    print 'evalute'
    return 3.14 * self.radius ** 2 

c = Circle(4)
print c.radius
print c.area
print c.area
print c.area

可以看到,'evalute'只输出了一次.如果看了我前面几篇博文,对@lazy的机制应该很好理解.
在这里,lazy类有__get__方法,说明是个描述器,第一次执行c.area的时候,因为顺序问题,先去c.__dict__中找,没找到,就去类空间找,在类Circle中,有area()方法,于是就被__get__拦截.
在__get__中,调用实例的area()方法算出结果,并动态给实例添加个同名属性把结果赋给它,即加到c.__dict__中去.
再次执行c.area的时候,先去c.__dict__找,因为此时已经有了,就不会经过area()方法和__get__了.

注意点
请注意以下代码场景:

代码片段1:
Python2.6代码

class Parrot(object):
  def __init__(self):
    self._voltage = 100000 

  @property
  def voltage(self):
    """Get the current voltage."""
    return self._voltage 

if __name__ == "__main__":
  # instance
  p = Parrot()
  # similarly invoke "getter" via @property
  print p.voltage
  # update, similarly invoke "setter"
  p.voltage = 12

代码片段2:
Python2.6代码

class Parrot:
  def __init__(self):
    self._voltage = 100000 

  @property
  def voltage(self):
    """Get the current voltage."""
    return self._voltage 

if __name__ == "__main__":
  # instance
  p = Parrot()
  # similarly invoke "getter" via @property
  print p.voltage
  # update, similarly invoke "setter"
  p.voltage = 12

代码1、2的区别在于

class Parrot(object):

在python2.6下,分别运行测试
片段1:将会提示一个预期的错误信息 AttributeError: can't set attribute
片段2:正确运行

参考python2.6文档,@property将提供一个ready-only property,以上代码没有提供对应的@voltage.setter,按理说片段2代码将提示运行错误,在python2.6文档中,我们可以找到以下信息:

BIF:
property([fget[, fset[, fdel[, doc]]]])
Return a property attribute for new-style classes (classes that derive from object).
原来在python2.6下,内置类型 object 并不是默认的基类,如果在定义类时,没有明确说明的话(代码片段2),我们定义的Parrot(代码片段2)将不会继承object

而object类正好提供了我们需要的@property功能,在文档中我们可以查到如下信息:

new-style class
Any class which inherits from object. This includes all built-in types like list and dict. Only new-style classes can use Python's newer, versatile features like __slots__, descriptors, properties, and __getattribute__().

同时我们也可以通过以下方法来验证
Python 2.6代码

class A:
  pass 

>>type(A)
<type 'classobj'>

Python 2.6代码

class A(object):
  pass 

>>type(A)
<type 'type'>

从返回的<type 'classobj'>,<type 'type'>可以看出<type 'type'>是我们需要的object类型(python 3.0 将object类作为默认基类,所以都将返回<type 'type'>)

为了考虑代码的python 版本过渡期的兼容性问题,我觉得应该定义class文件的时候,都应该显式定义object,做为一个好习惯

最后的代码将如下:

class Parrot(object):
  def __init__(self):
    self._voltage = 100000 

  @property
  def voltage(self):
    """Get the current voltage."""
    return self._voltage 

  @voltage.setter
  def voltage(self, new_value):
    self._voltage = new_value 

if __name__ == "__main__":
  # instance
  p = Parrot()
  # similarly invoke "getter" via @property
  print p.voltage
  # update, similarly invoke "setter"
  p.voltage = 12

另外,@property是在2.6、3.0新增的,2.5没有该功能。

(0)

相关推荐

  • 浅析Python编写函数装饰器

    编写函数装饰器 本节主要介绍编写函数装饰器的相关内容. 跟踪调用 如下代码定义并应用一个函数装饰器,来统计对装饰的函数的调用次数,并且针对每一次调用打印跟踪信息. class tracer: def __init__(self,func): self.calls = 0 self.func = func def __call__(self,*args): self.calls += 1 print('call %s to %s' %(self.calls, self.func.__name__)

  • Python的装饰器用法学习笔记

    在python中常看到在定义函数是使用@func. 这就是装饰器, 装饰器是把一个函数作为参数的函数,常常用于扩展已有函数,即不改变当前函数状态下增加功能. def run(): print "I'm run." 我有这么一个函数, 我想知道这个函数什么时候开始什么时候结束. 我应该这么写 def run(): print time.ctime() print "I'm run." print time.ctime() 但是如果不允许修改函数的话就需要装饰器了 de

  • 简析Python的闭包和装饰器

    什么是装饰器? 装饰器(Decorator)相对简单,咱们先介绍它:"装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数",听起来有点绕,没关系,直接看示意图,其中 a 为与装饰器 @a 对应的函数, b 为装饰器修饰的函数,装饰器@a的作用是: 简而言之:@a 就是将 b 传递给 a(),并返回新的 b = a(b) 栗子: 上面使用@dobi来表示装饰器,其等同于:qinfeng = dobi(qinfeng) 因此装饰器本质

  • 深入理解Python装饰器

    装饰器简介: 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 装饰器最早在Python 2.5中出现,它最初被用于加工函数和方法这样的可调用对象(callable object,这样的对象定义有__call__方法).在Python 2

  • 深入理解Python中装饰器的用法

    因为函数或类都是对象,它们也能被四处传递.它们又是可变对象,可以被更改.在函数或类对象创建后但绑定到名字前更改之的行为为装饰(decorator). "装饰器"后隐藏了两种意思--一是函数起了装饰作用,例如,执行真正的工作,另一个是依附于装饰器语法的表达式,例如,at符号和装饰函数的名称. 函数可以通过函数装饰器语法装饰: @decorator # ② def function(): # ① pass 函数以标准方式定义.① 以@做为定义为装饰器函数前缀的表达式②.在 @ 后的部分必须

  • Python的几个高级语法概念浅析(lambda表达式闭包装饰器)

    1. 匿名函数 匿名函数(anonymous function)是指未与任何标识符绑定的函数,多用在functional programming languages领域,典型应用场合: 1) 作为参数传给高阶函数(higher-order function ),如python中的built-in函数filter/map/reduce都是典型的高阶函数 2) 作为高阶函数的返回值(虽然此处的"值"实际上是个函数对象) 与命名函数(named function)相比,若函数只被调用1次或有

  • 详解Python装饰器由浅入深

    装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们以装饰函数为例子介绍其用法.要理解在Python中装饰器的原理,需要一步一步来.本文尽量描述得浅显易懂,从最基础的内容讲起. (注:以下使用Python3.5.1环境) 一.Python的函数相关基础 第一,必须强调的是python是从上往下顺序执行的,而且碰到函数的定义代码块是不会立即执行它的,只

  • 12步教你理解Python装饰器

    通过下面的步骤让你由浅入深明白装饰器是什么.假定你拥有最基本的Python知识,本文阐述的东西可能对那些在工作中经常接触Python的人有很大的帮助. 1.函数(Functions) 在Python里,函数是用def关键字后跟一个函数名称和一个可选的参数表列来创建的,可以用关键字return指定返回值.下面让我们创建和调用一个最简单的函数: >>> def foo(): ... return 1 >>> foo() 1 该函数的函数体(在Python里将就是多行语句)是

  • Python装饰器基础详解

    装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 前面快速介绍了装饰器的语法,在这里,我们将深入装饰器内部工作机制,更详细更系统地介绍装饰器的内容,并学习自己编写新的装饰器的更多高级语法. 什么是装饰器 装饰是为函数和类指定管理代码的一种

  • 深入理解python中的闭包和装饰器

    python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python2.7,其他版本可能存在差异. 也许直接看定义并不太能明白,下面我们先来看一下什么叫做内部函数: def wai_hanshu(canshu_1): def nei_hanshu(canshu_2): # 我在函数内部有定义了一个函数 return canshu_1*canshu_2 return

  • python装饰器初探(推荐)

    一.含有一个装饰器 #encoding: utf-8 ############含有一个装饰器######### def outer(func): def inner(*args, **kwargs):#要装饰f1(),这里用这俩形式参数,可以接受任意个参数,不管f1定义几个参数 print "1" r = func(*args, **kwargs)#这里要用func,不要用f1 print "2" return r return inner @outer #这里ou

随机推荐