插件和过滤器装饰器开发中的感悟-python-django

写这篇随笔是因为今天自己在写插件和过滤方法的过程中碰壁了,折腾了好久终于稍微发现些问题,在此记下,以作备忘。

在看了xadmin的插件机制后,笔者也想使用该思想来扩展kadmin中视图的方法。

例如,在一个登陆视图中,一般的逻辑如下:

class LoginView(BaseAdminView):
    ‘‘‘登陆视图‘‘‘
    auth_form=None#用于认证的表单类
    login_template=None
    title=""

    def update_login_params(self,defaults):
        ‘‘‘ 用于在执行login视图函数前修改参数的钩子‘‘‘
        return defaults
    @never_cache
    def get(self,request,*arg,**kwargs):
        from django.contrib.auth.views import login
        context=self.get_context(request,*arg,**kwargs)#获取父类的context
        context.update({
            ‘title‘:self.title,
            ‘app_path‘:request.get_full_path,
            REDIRECT_FIELD_NAME:request.get_full_path(),
        })
        defaults={
            ‘extra_context‘:context,
            ‘authentication_form‘:self.auth_form or AdminAuthenticationForm,
            ‘template_name‘:self.login_template or ‘kadmin/views/login.html‘,
        }
       defaults=self.update_login_params(defaults) 

       return login(request,**defaults) @never_cache def post(self,request,*arg,**kwargs): return self.get(request,*arg,**kwargs)

我们希望能通过update_login_params方法,在执行login函数前更新context,就应该使用插件来接管该方法的执行以按需要进行一些修改。

这里有两种思路:

1、使用过滤器,过滤update_login_params的结果并返回

2、使用插件函数,控制update_login_params的执行(一般是活得update_login_params的执行结果 传入插件函数 由插件函数进行一些修改后再返回)

也可以用插件函数接管update_login_params,并自行决定是否执行update_login_params,或是在update_login_params执行前或后进行一些行为

从上述两点看,插件更为灵活和自由,控制范围更大,二过滤器只能就update_login_params的返回值进行操作并返回。

1、过滤器

定义过滤器装饰器@filter_hook

def filter_hook(func):
    #过滤器装饰器
    #用于对被hook的方法返回值进行再处理
    func_name=func.__name__
    @wraps(func)
    def inner_func(self,*arg,**kwargs):
        if getattr(self,‘filters‘,None):
            filter_infos=[(getattr(fi,‘priority‘,10),fi) for fi in self.filters if fi.__name__==func_name]
            filters=[fi for p,fi in sorted(filter_infos,key=lambda x:x[0],reverse=False)]
            result=func(self,*arg,**kwargs)
            for fi in filters:
                result=fi(self,result,*arg,**kwargs)
            return result
        else:
            return func(self,*arg,**kwargs)
    return inner_func

源码说明:在视图对象self上遍历filters 得到过滤函数,过滤结果并返回结果

例如我们可以定义一个过滤器给say_hello的返回值加上括号

@set_attr(attr_name=‘__name__‘,value=‘say_hello‘)
def add_tag(self,result,o,*arg,**kwargs):
    return "(%s)"%result
class Person:
    name=‘akun‘
    sex="akun-male"
    plugins=[MalePlugin(sex_num) for sex_num in range(4)]
    filters=[add_tag]

    @filter_hook
    @plugin_hook
    def say_hello(self,go,*arg,**kwargs):
        print("-----------done--------")
        return "Hello i am %s"%self.name
if __name__=="__main__":
    p=Person()
    print(p.say_hello(‘go‘))

说明:@set_attr是一个装饰器,负责设置函数的属性值,因为filter_hook在查找过滤函数时是找被hook的同名函数,所以需要把add_tag的__name__设为say_hello

2.插件

定义插件装饰器:

def plugin_chain(funcs,token,func,*arg,**kwargs):
    if token==-1:
        return func()
    @wraps(func)
    def _inner_func():
        wrap_func=funcs[token]
        arg_specs=getargspec(wrap_func)[0]
        if len(arg_specs)==1:
            func()
            return wrap_func(*arg,**kwargs)
        elif len(arg_specs)>=2 and arg_specs[1]=="__":
            back=func
        else:
            back=func()
        return wrap_func(back,*arg,**kwargs)
    return plugin_chain(funcs,token-1,_inner_func,*arg,**kwargs)

def plugin_hook(func):
    func_name=func.__name__
    @wraps(func)
    def method(self,*arg,**kwargs):
        @wraps(func)
        def _inner_func():
            return func(self,*arg,**kwargs)
        if getattr(self,‘plugins‘,None):
            plugin_funcs=[(getattr(p,func_name),getattr(getattr(p,func_name),‘priority‘,10))
                      for p in self.plugins if getattr(p,func_name,None) ]
            print(plugin_funcs)
            #对插件方法按照priority升序排列a
            plugin_funcs=[p for p,priority in sorted(plugin_funcs,key=lambda x:x[1],reverse=False)]
            return plugin_chain(plugin_funcs,len(plugin_funcs)-1,_inner_func,*arg,**kwargs)
        else:
            return func(self,*arg,**kwargs)
    return method

源码说明:类似于@filter_hook只是,注意

        wrap_func=funcs[token]
        arg_specs=getargspec(wrap_func)[0]
        if len(arg_specs)==1:
            func()
            return wrap_func(*arg,**kwargs)
        elif len(arg_specs)>=2 and arg_specs[1]=="__":
            back=func
        else:
            back=func()
        return wrap_func(back,*arg,**kwargs)

如果若用于接收被hook方法的返回值的参数名为"__"则,__会被设置为被hook的方法,也就是可以通过插件来接管被hook的方法。

我们定义一个插件来在执行say_hello前打印出被hook的方法

class BeforeHook:
    @set_attr(attr_name=‘priority‘,value=6)
    def say_hello(self,__,o,*arg,**kwargs):
        print("the back is %s"%__)
        return __(*arg,**kwargs)

这里的__或被设置为被hook的方法

时间: 10-01

插件和过滤器装饰器开发中的感悟-python-django的相关文章

流之阅读器和书写器(过滤器阅读器和过滤器书写器)

InputStreamReader和OutputStreamWriter类就是相当于输入和输出流之上的装饰器,把面向字节的接口改为面向字符的接口.完成之后,就可以在它们之上使用面向字符的过滤器阅读器或过滤器书写器.与过滤器流一样,有很多子类可以完成特定的过滤工作,包括: BufferedReader BufferedWriter LineNumberReader PushbackReader PrintWriter BufferedReader和BufferedWriter类是基于字符的,对应于

视频播放器开发中遇到的一些小问题MPMoviePlayerController

1 开发环境是 xcode6  ipad3真机 ios8.1.1越狱 需要添加以下代码  ,否则真机测试没有外音,只有耳机 NSError *setCategoryError = nil; [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryError]; if (setCategoryError) {         //handle error }AVA

Python 中的装饰器

说到装饰器是我们每个学Python人中的心痛. 装饰器作用:是用来装饰其他函数的,为其他函数添加新功能. 原则:1.不能改变被修饰函数的源代码. 2.不能修改被修饰函数的调用方式. 学装饰器前还需要了解的东西: 返回值(return): return 语句是可选的,它可以定义在函数体的任意位置表示函数调用到此结束.如果没有return语句输出的是none,如果有return语句但是return后没有跟东西,也是返回的是none. 注意:(1)要想看到return后边的值,需要打印出来print(

理解Python中的装饰器

文章先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: @makebold @makeitalic def say(): return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出的答案是: def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>&q

python cookbook 学习系列(一) python中的装饰器

简介 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能,我们也称之为AOP(面向切面编程) 原理 实现装饰器的最主要的原因是python中一切皆为对象,我们会把方法看做一个对

8.Python中装饰器是什么?

Python中装饰器是什么? A Python decorator is a specific change that we make in Python syntax to alter functions easily. Python decorator是我们在Python语法中使用的一个特定的更改,可以轻松地改变函数. http://www.cnblogs.com/zoe233/p/7070067.html 装饰器定义: 本质是函数.函数的目的是为了完成特定的功能,那么装饰器的功能是什么呢?

浅显易懂的谈一谈python中的装饰器!!

hello大家好~~我是稀里糊涂林老冷,一天天稀里糊涂的. 前一段时间学习了装饰器,觉着这东西好高大上哇靠!!哈哈,一定要总结一下,方便以后自己查阅,也希望帮助其他伙伴们共同进步! 装饰器: 大家可以这样理解,装饰器是运用闭包的基本原理,对一个目标函数进行装饰.即是在执行一个目标函数之前.之后执行一些特定的事情. 学习装饰器一定要有闭包的基础知识,如果对闭包不是特别理解的话,可以参考我之前的博文http://www.cnblogs.com/Lin-Yi/p/7305364.html,也可以学习其

装饰器 以及 django 中的应用

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用. # 简单的 装饰器 from functools import wraps # 装饰器修复 def wrapper(func): @wraps(func) # 装饰器修复 def

python中装饰器修复技术

python装饰器@wraps作用-修复被装饰后的函数名等属性的改变 Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变), 为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用.写一个decorator的时候,最好在实现之前加上functools的wrap, 它能保留原有函数的名称和docstring. 未加@wraps的时候: from functools impor