插件和过滤器装饰器开发中的感悟-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的方法
注意:
正是因为@plugin_hook是需要获取argspecs来得到位置参数的名称,用于判断插件函数是否接受被hook方法的返回值,以及是否在argsepcs[0]中是否
含有双下划线位置参数,如果有则把被hook的方法交给插件函数 而不是把结果交出去。所以基于以上两点,我们不能@filter_hook一个插件方法,因为那样会
包装一个新函数(参数列表为 self,*arg,**kwargs)给plugin_hook,那样的话plugin_hook得到的argsepcs[0]只有self,会认为插件方法不接受返回值,于是
直接调用
if len(arg_specs)==1:
func()
return wrap_func(*arg,**kwargs)
故会产生错误
但是我们可以在被hook的方法上@filter_hook和@plugin_hook任意组合
插件和过滤器装饰器开发中的感悟-python-django的更多相关文章
- day20-Python运维开发基础(装饰器 / 类中的方法 / 类的方法变属性)
1. 装饰器 / 类中的方法 / 类的方法变属性 # ### 装饰器 """ 定义:装饰器用于拓展原来函数功能的一种语法,返回新函数替换旧函数 优点:在不更改原函数代码的 ...
- 理解Python中的装饰器//这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档
转自:http://www.cnblogs.com/rollenholt/archive/2012/05/02/2479833.html 这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档 ...
- python中闭包和装饰器的理解(关于python中闭包和装饰器解释最好的文章)
转载:http://python.jobbole.com/81683/ 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需 ...
- Django模板自定义标签和过滤器,模板继承(extend),Django的模型层
上回精彩回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...
- 视频播放器开发中遇到的一些小问题MPMoviePlayerController
1 开发环境是 xcode6 ipad3真机 ios8.1.1越狱 需要添加以下代码 ,否则真机测试没有外音,只有耳机 NSError *setCategoryError = nil; ...
- Pytest的装饰器——parametrize中ids里包含中文,用例标题显示异常如何解决?
在使用pytest做测试的过程中,经常会用到pytest.mark.parametrize来对批量生成测试用例,比如 @pytest.mark.parametrize( ['a', 'b', 'exp ...
- Day04 - Python 迭代器、装饰器、软件开发规范
1. 列表生成式 实现对列表中每个数值都加一 第一种,使用for循环,取列表中的值,值加一后,添加到一空列表中,并将新列表赋值给原列表 >>> a = [0, 1, 2, 3, 4, ...
- python中的闭包和装饰器
重新学习完了函数,是时候将其中的一些重点重新捋一捋了,本次总结的东西只有闭包和装饰器 1.闭包 闭包是python函数中的一个比较重要功能,一般闭包都是用在装饰器上,一般学完闭包就会去学习装饰器,这俩 ...
- JAVASCRIPT中装饰器是什么(装修)
装饰器是什么? 解码器是将另一段代码包装在一个代码中的简单方法. 这个概念类似于你以前听说过的功能成分和高阶成分. 这在许多情况下都被使用过,也就是说,成都装修公司简单地将一个函数包装到另一个函数中: ...
随机推荐
- 352. Data Stream as Disjoint Intervals
Plz take my miserable life T T. 和57 insert interval一样的,只不过insert好多. 可以直接用57的做法一个一个加,然后如果数据大的话,要用tree ...
- c#基础语言编程-异常处理
异常的定义 异常就是程序中的运行时错误,当出现异常时,系统会捕获这个错误并抛出一个异常.若程序没有提供处理该异常的代码,系统会挂起这个程序. 常见异常的类型 System.Exception 最泛化的 ...
- C#中的线程(上)-入门 分类: C# 线程 2015-03-09 10:56 53人阅读 评论(0) 收藏
1. 概述与概念 C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行.一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为"主线 ...
- js 中cookie 使用
一个系统有多种 角色, 每一种角色不同权限.后台请求的数据根据权限展示 ,所以要把权限保存在浏览器中. 首先 引入 在页面 <script type="text/javascript& ...
- [struts2学习笔记] 第一节 关于struts2的简单认知
本文地址:http://blog.csdn.net/sushengmiyan/article/details/40298287 官方文档:http://struts.apache.org/releas ...
- crontab 定时任务 每过多少分钟执行
被下面的问题纠结了一会 记录一下以备提醒 每过5分钟执行 */ * * * * curl localhost/system/dns/dnns.php?ac=ToDNS 每小时的第五分钟执行 0 * * ...
- Qt 学习之路:Canvas
在 QML 刚刚被引入到 Qt 4 的那段时间,人们往往在讨论 Qt Quick 是不是需要一个椭圆组件.由此,人们又联想到,是不是还需要其它的形状?这种没玩没了的联想导致了一个最直接的结果:除了圆角 ...
- iOS-iPad开发之SplitViewController简单介绍
iOS-iPad开发之SplitViewController简单介绍 SplitViewController图形化创建 SplitViewController可以并列显示两个view,适用于基于nav ...
- (转载)Windows 7 Ultimate(旗舰版)SP1 32/64位官方原版下载(2011年5月12日更新版)
MSDN于2011年5月12日,最新发布简体中文Windows 7 Ultimate 旗舰版 SP1 DVD镜像安装包,分32位和64位两个版本.最新发行代号分别是:677486(32位),67740 ...
- eclipse4.3 kepler中安装maven
1.软件准备 a:Eclipse 4.3 http://www.eclipse.org/downloads/ b:maven http://maven.apache.org/download.cgi ...