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

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

装饰器和装饰器模式

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

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同步windows和linux文件

写python脚本的初衷,每次在windows编辑完文件后,想同步到linux上去,只能够登录服务器,...

爬虫基本原理

爬虫基本原理 一、爬虫是什么? 百度百科和维基百科对网络爬虫的定义:简单来说爬虫就是抓取目标网站内容的工具,一般是根据定义的行...

Django 函数和方法的区别

函数和方法的区别 1、函数要手动传self,方法不用传 2、如果是一个函数,用类名去调用,如果是一个方法...

Django 知识补漏单例模式

单例模式:(说白了就是)创建一个类的实例。在 Python 中,我们可以用多种方法来实现单例模式&#x...

Django基础知识MTV

Django简介 Django是使用Python编写的一个开源Web框架。可以用它来快速搭建一个高性能的网站。 Django也是一个MVC框架。但是在Dj...

Python mysql 索引原理与慢查询优化

一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,...

发表评论

访客

看不清,换一张

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