首页 > 代码库 > python 高级一点的用法,猴子补丁与元类
python 高级一点的用法,猴子补丁与元类
好久没更新了,今天想想哪些要记录下的,装饰器什么的就不说了,很熟悉了,记录下。
1.monkey patch.
其实就是动态修改类,包括属性方法等的一种方式。
比如a = A() a.foo = foo之类的,但是怎么在运行前修改呢,类似gevent那样用自己的socket替换,
gevent 源码是这样的
from eventlet.green import socketpatcher.inject(‘ftplib‘, globals(), (‘socket‘, socket))
然后inject里sys.modules[
‘__patched_module_‘
+
module_name]
=
module这个关键的语句。
也就是说如果要实现这样统一入口的地方patch,就要明白python 寻找变量的方式,LEGB也就是说当python在载入模块的时候,sys.modules里会载入,然后按照LEGB的原则添加到当前模块
这里举一个实际的例子,如果有一天产品需要在所有搜索界面实现一个公用功能,比如记住当前页活着搜索结果,跳转回来也会有,页面涉及几十个甚至上百个,怎么办呢,当然一饿一个改也灭有问题,
但是这样改工作量大,并且容易出错,测试覆盖点多,周期会很长,所以就可以这样里,看下面
def _wraper(fn):
def _render(*args, **kwagrs):
request = args[0]
http_content = args[2]
page = request.GET.get(‘dt_page‘, 0)
try:
page = int(page)
except:
page = 0
http_content.update({‘dt_page‘: page})
return fn(*args, **kwagrs)
return _render
__import__("django.shortcuts")
sys = __import__("sys")
_render_module = sys.modules[‘django.shortcuts‘]
_render_module.render = _wraper(_render_module.render)
因为每个页面都要通过页面渲染,所以直接载入
django.shortcuts并且修改它的render方法变成自己装饰里的render,当然我只是增加了一个dt_page参数而已,在前端点击时候传入参数,当然前端也差不多,就是在js以及页面加载完成后,
用dom.find选择器的方式然后修改里所有链接并加上dt_page参数就实现效果啦,哈哈是不是很神奇。这就是monkeypatch啦,gevent也是这样来替换系统的socket库的。
下面介绍另一种动态修改类的方式。
2.元类
这个比较复杂,我也只是了解,实际当中并咩用到,其实django的ORM就是用元类来实现的。
其实要理解这个,直接就看太抽象了。下面分开说。
1)先有鸡还是先有蛋,object与type
object类是所有新式类的父类。
type是所有类的类
当然object 本身与type之间关系可能比较模糊:
>>> object.__class__
<type ‘type‘>
>>> object.__bases__ # object 无父类,因为它是链条顶端。
()
- type是一种object, type is kind of object。即Type是object的子类。
>>> type.__bases__
(<type ‘object‘>,)
>>> type.__class__ # type的类型是自己
<type ‘type‘>
所以在Python的世界中,object是父子关系的顶端,所有的数据类型的父类都是它;type是类型实例关系的顶端,所有对象都是它的实例的。
也就是说类本身是实例,它是依靠type生成类实力,与其它编译型语言java,C#不一样,因为它们只有object却没有type类,所以它们不能实现动态修改类。
当然经典类的元类就是继承type的类classobj来生成的,而新式类(继承object)就是由type生成的(如果不手动指定元类的话)。有了这个,想必大家都知道为啥能用元类修改类类吧。好,下面来试试,自定义元类。
2)首先有一种很简单的方式生成一个类,直接用type,
Foo = type(‘Foo‘, (), {}) 这样就能直接生成一个FOO类。其实就是相当于用type生成了一个FOO类,跟class FOO一个意思,但是平常不这么用。我们怎么动态修改呢?用
关键字__metaclass__就能指定用哪一个类去创建当前类,
class MyMetaClass(type):
def __new__(cls
, name, bases, dct
):
class MyClass(object):
__metaclass__ = MyMetaClass
pass
这样创建的MyClass就是由MyMetaClass来创建的,在MyMetaClass里就可以修改你想创建的类了,这样理解起来是不是不那么抽象了,里面的参数查查API就明白了,用时在查。
python 高级一点的用法,猴子补丁与元类