python -- 装饰器的高级应用

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

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()

  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值