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

      

  • 相关阅读:
    linux 程序后台运行
    小型网站架构技术点(简要)
    rsync安装与配置使用 数据同步方案(centos6.5)
    nfs的原理 安装配置方法 centos6.5
    centos 6.5 升级到 python2.7
    ntpdate 设置时区(注意本地时区要设置正确)
    关于umask的计算方式(简单任性)
    No space left on device(总结)
    lsof 查看打开了一个文件的有哪些进程 统计那个进程打开的文件最多
    作用域是什么?
  • 原文地址:https://www.cnblogs.com/abclife/p/7463354.html
Copyright © 2020-2023  润新知