装饰器和装饰器模式
装饰器模式是面向对象的一种设计模式,支持将行为动态增加到已经存在的对象上。当装饰一个对象的时候,就表示独立与其他类实例对象,为该对象扩展了新的功能。

python的装饰器不是装饰器模式的一种实现。python装饰器是在定义的时候对函数或方法增加功能,而不是在运行的时候增加。
装饰器模式可以在python中实现,但是这样做意义不大。因为python是鸭子类型风格的编程语言。鸭子类型(英语:duck typing)是动态类型的一种风格。

一个基础的装饰器

import time
import datetime def time_this(original_func):
def new_func(*args, **kwargs):
start_a = datetime.datetime.now()
x = original_func(*args, **kwargs)
end_a = datetime.datetime.now()
print("Elapsed Time = {0}".format(start_a - end_a))
return x
return new_func @time_this
def func_a(stuff):
print("i need a sleep.")
time.sleep(3) func_a(1)

运行结果:

i need a sleep.
Elapsed Time = -1 day, 23:59:56.999700

带有参数的装饰器
有时候,除了完成其装饰的函数外,还可以带上参数。这种技术常用于注册类似功能。比如:

@view_config(route_name='home',renderer='templates/mytemplate.pt')
def my_view(request):
return {'project':'hello decorators'}

假设有个应用,用户可以通过gui登录。用户和gui触发时间交互调用python函数。不同的用户有不同的权限。执行不同的函数需要不同了类型的权限。比如:

#assume these functions exist
def current_user_id():
"""
this function returns the current logged in user id, if the user is not authenticated then return None
""" def get_permissions(iUserId):
"""
returns a list of permission strings for the given user. For example ['logged_in','administrator','premium_member']
""" #we need to implment permission checking on these functions def delete_user(iUserId):
"""
delete the user with the given Id. This function is only accessable to users with administrator permissions
""" def new_game():
"""
any logged in user can start a new game
""" def premium_checkpoint():
"""
save the game progress, only accessable to premium members
"""

实现的方法之一是使用多个装饰器:

def requires_admin(func):
def ret_func(*args,**kwargs):
permissions = get_permissions(current_user_id())
if 'administrator' in permissions:
return func(*args,**kwargs)
else:
raise Exception("Not allowed")
return ret_func def requires_logged_in(func):
def ret_func(*args,**kwargs):
permissions = get_permissions(current_user_id())
if 'logged_in' in permissions:
return func(*args,**kwargs)
else:
raise Exception("Not allowed")
return ret_func def requires_premium_member(func):
def ret_func(*args,**kwargs):
permissions = get_permissions(current_user_id())
if 'premium_member' in permissions:
return func(*args,**kwargs)
else:
raise Exception("Not allowed")
return ret_func @requires_admin
def delete_user(iUserId):
"""
delete the user with the given Id. This function is only accessable to users with administrator permissions
""" @requires_logged_in
def new_game():
"""
any logged in user can start a new game
""" @requires_premium_member
def premium_checkpoint():
"""
save the game progress, only accessable to premium members
"""

但是这样的话,需要多个装饰器。如果有权限检查模块程序发生变动,就需要逐一修改装饰器。难道不可以通过一个装饰器来实现么?

答案是:有。我们需要一个返回结果是装饰器的函数。

def requires_permission(sPermission):
def decorator(func):
def decorated(*args,**kwargs):
permissions = get_permissions(current_user_id())
if sPermission in permissions:
return func(*args,**kwargs)
raise Exception("permission denied")
return decorated
return decorator def get_permissions(iUserId): #this is here so that the decorator doesn't throw NameErrors
return ['logged_in',] def current_user_id(): #ditto on the NameErrors
return 1 #and now we can decorate stuff... @requires_permission('administrator')
def delete_user(iUserId):
"""
delete the user with the given Id. This function is only accessible to users with administrator permissions
""" @requires_permission('logged_in')
def new_game():
"""
any logged in user can start a new game
""" @requires_permission('premium_member')
def premium_checkpoint():
"""
save the game progress, only accessable to premium members
"""

  

通用的装饰器代码示例:

def outer_decorator(*outer_args,**outer_kwargs):
def decorator(func):
def decorated(*args,**kwargs):
do_something(*outer_args,**outer_kwargs)
return func(*args,**kwargs)
return decorated
return decorator @outer_decorator(1,2,3)
def foo(a,b,c):
print a
print b
print c foo()

等价于:

def decorator(func):
def decorated(*args,**kwargs):
do_something(1,2,3)
return func(*args,**kwargs)
return decorated
return decorator @decorator
def foo(a,b,c):
print a
print b
print c foo()

装饰类
装饰器并不仅仅限于装饰函数,也可以装饰类。
假如我们有个类,需要完成很多重要工作,我们想计时这个类完成每项工作需要的时间。我们可以使用上面定义好的time_this:

class ImportantStuff(object):
@time_this
def do_stuff_1(self):
...
@time_this
def do_stuff_2(self):
...
@time_this
def do_stuff_3(self):
...

上面这样做是可以实现,但是要添加很多额外的代码行在类定义中。
如果我们写了很多类的方法,忘记了其中对其中个别函数进行装饰怎么办?或者如果我们不再需要计时功能呢。
可使用以下的方式进行优化:

@time_all_class_methods
class ImportantStuff:
def do_stuff_1(self):
...
def do_stuff_2(self):
...
def do_stuff_3(self):
...

上面的代码等价于:

class ImportantStuff:
def do_stuff_1(self):
...
def do_stuff_2(self):
...
def do_stuff_3(self):
... ImportantStuff = time_all_class_methods(ImportantStuff)

那么time_all_class_methods是如何工作的呢?
首先、它需要一个类作为参数,并返回一个类。返回的类的功能看起来应该和原先的importstuff类类似。这里我们可以这么做:

import datetime
import time def time_this(original_func):
print ("decorating")
def new_func(*args,**kwargs):
print("starting timer")
start = datetime.datetime.now()
x = original_func(*args,**kwargs)
end = datetime.datetime.now()
print "Elapsed Time = {0}".format(end-start)
return x
return new_func def time_all_class_methods(Cls):
class NewCls(object):
def __init__(self,*args,**kwargs):
self.oInstance = Cls(*args,**kwargs)
def __getattribute__(self,s):
"""
this is called whenever any attribute of a NewCls object is accessed. This function first tries to
get the attribute off NewCls. If it fails then it tries to fetch the attribute from self.oInstance (an
instance of the decorated class). If it manages to fetch the attribute from self.oInstance, and
the attribute is an instance method then `time_this` is applied.
"""
try:
x = super(NewCls,self).__getattribute__(s)
except AttributeError:
pass
else:
return x
x = self.oInstance.__getattribute__(s)
if type(x) == type(self.__init__): # it is an instance method
return time_this(x) # this is equivalent of just decorating the method with time_this
else:
return x
return NewCls #now lets make a dummy class to test it out on: @time_all_class_methods
class Foo(object):
def a(self):
print "entering a"
import time
time.sleep(3)
print "exiting a" oF = Foo()
oF.a()

  

python -- 装饰器的高级应用的更多相关文章

  1. Python装饰器的高级用法(翻译)

    原文地址 https://www.codementor.io/python/tutorial/advanced-use-python-decorators-class-function 介绍 我写这篇 ...

  2. Python装饰器由浅入深

    装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们 ...

  3. Python高级特性: 12步轻松搞定Python装饰器

    12步轻松搞定Python装饰器 通过 Python 装饰器实现DRY(不重复代码)原则:  http://python.jobbole.com/84151/   基本上一开始很难搞定python的装 ...

  4. python 装饰器 一篇就能讲清楚

    装饰器一直是我们学习python难以理解并且纠结的问题,想要弄明白装饰器,必须理解一下函数式编程概念,并且对python中函数调用语法中的特性有所了解,使用装饰器非常简单,但是写装饰器却很复杂.为了讲 ...

  5. Python第二十六天 python装饰器

    Python第二十六天 python装饰器 装饰器Python 2.4 开始提供了装饰器( decorator ),装饰器作为修改函数的一种便捷方式,为工程师编写程序提供了便利性和灵活性装饰器本质上就 ...

  6. 利用世界杯,读懂 Python 装饰器

    Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性, 熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic. 今天就结合最近的世界杯带大家理解下装饰器. 德 ...

  7. Python装饰器探险

    关于python装饰器的理解和用法,推荐廖雪峰老师和这一篇博客以及知乎 以下代码均已手动敲过,看完本篇内容,包你装饰器小成! 装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就 ...

  8. Python 装饰器入门(上)

    翻译前想说的话: 这是一篇介绍python装饰器的文章,对比之前看到的类似介绍装饰器的文章,个人认为无人可出其右,文章由浅到深,由函数介绍到装饰器的高级应用,每个介绍必有例子说明.文章太长,看完原文后 ...

  9. Python学习:11.Python装饰器讲解(二)

    回顾 上一节我们进行了Python简单装饰器的讲解,但是python的装饰器还有一部分高级的使用方式,这一节就针对python装饰器高级部分进行讲解. 为一个函数添加多个装饰器 今天,老板又交给你一个 ...

随机推荐

  1. 面向对象JavaScript的基本概念

    一.类和对象 1, JavaScript对象分为三类:本地对象,内置对象,宿主对象. 本地对象有15种:Object Function Array String Number Boolean Date ...

  2. 51nod_1831: 小C的游戏(Bash博弈 找规律)

    题目链接 此类博弈不需要考虑sg函数,只需要确定必胜态和必败态,解题思路一般为打败先打表找规律,而后找规律给出统一的公式.打表方式:给定初始条件(此题中为ok[0]=ok[1]=0),然后从低到高枚举 ...

  3. C#监控类属性的更改(大花猫动了哪些小玩具)

    C#监控类属性的更改(大花猫动了哪些小玩具) 实体类创建后在方法中对哪些属性赋值了,传递到底层方法时在底层如何得知哪些属性被赋值过.如何监控属性的更改,请看脑洞大开之<大花猫动了哪些小玩具> ...

  4. javascript中的闭包(Closure)的学习

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 下面是我在网上通过学习阮一峰老师的笔记,感觉总结很不错,特记录于此. 一.变量的作用域 要理解 ...

  5. echarts_部分图表配置_堆叠折线图

    echarts基本图表使用: 1.获取包裹元素(var myChart = echarts.init(document.getElementById('thisId'));)2.设置option(op ...

  6. 终结 finalize()和垃圾回收(garbage collection)

    1.为什么要有finalize()方法? 假定你的对象(并非使用new)获得了一块"特殊"的内存区域,由于垃圾回收器只知道释放那些经由new分配的内存,所以他不知道该如何释放该对象 ...

  7. python-希尔排序

    希尔排序 希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因DL.Shell于1959年提出而得名. 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本.希尔排序是非稳定排序算法 ...

  8. 35. leetcode 501. Find Mode in Binary Search Tree

    501. Find Mode in Binary Search Tree Given a binary search tree (BST) with duplicates, find all the  ...

  9. vue基础一

    一.vue的编写步骤 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  10. Ubuntu虚拟机可以上网,可以ping网络,但是无法update和install,报连接不上服务器的错误解决方案:

    http://askubuntu.com/questions/711889/err-apt-get-update-on-ubuntu-14-04即使给Ubuntu配置了代理,terminal能ping ...