python基础教程:装饰器的高级应用

枫铃3年前 (2021-09-30)Python271

装饰器和装饰器模式

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

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>def delete_user(iUserId):
   """
   delete the user with the given Id. This function is>def new_game():
    """
    any logged in user can start a new game
    """
     
def premium_checkpoint():
   """
   save the game progress,>'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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>def new_game():
    """
    any logged in user can start a new game
    """
     
@requires_premium_member
def premium_checkpoint():
   """
   save the game progress,>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>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>('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,>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()

等价于:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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>class Foo(object):
    def a(self):
        print "entering a"
        import time
        time.sleep(3)
        print "exiting a"
 
oF = Foo()
oF.a()

相关文章

Python中的迭代遍历 for in

Python中的迭代遍历 for in

遍历就是从头到尾依次从列...

Python字符串逐字符或逐词反转方法

这篇文章主要介绍了Pyt...

python多种推导式的实现

根据一个列表,创建一个新...

python调用shell命令之三慷慨法

preface: 忙于近期的任务,须要用到libsvm的一些命令。如在终端执行javasvm_train train_file model_fi...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。