什么是Hook,就是在一个已有的方法上加入一些钩子,使得在该方法执行前或执行后另在做一些额外的处理,那么Hook技巧有什么作用以及我们为什么需要使用它呢,事实上如果一个项目在设计架构时考虑的足够充分,模块抽象的足够合理,设计之初为以后的扩展预留了足够的接口,那么我们完全可以不需要Hook技巧。但恰恰架构人员在项目设计之初往往没办法想的足够的深远,使得后续在扩展时深圳面临重构的痛苦,这时Hook技巧似乎可以为我们带来一记缓兵之计,通过对旧的架构进行加钩子来满足新的扩展需求。
下面我们就来看看如果进行Hook处理,我们按照Hook的对象的层级来逐一介绍
对类进行Hook
也就是说我们得钩子需要监控到类的创建等操作,然后在此之前或之后做我们希望的操作
1、Hook类的创建
你可以在写一个类的时候为其添加属性
class Foo(Bar): = …
创建类的过程是这样的:
Foo中有这个属性吗?如果是,会在内存中通过创建一个名字为Foo的类。如果没有找到,它会继续在Bar(父类)中寻找属性,并尝试做和前面同样的操作。如果在任何父类中都找不到,它就会在模块层次中去寻找,并尝试做同样的操作。如果还是找不到,就会用内置的type来创建这个类对象。
所以我们需要在给属性的值是一个能够创建一个类的东西,即一个继承type的类。
比如:
class (type): (cls, name, bases, dict): super(, cls).(name, bases, dict) cls. = None (cls, *args, **kw): if cls. is None: cls. = super(, cls).(*args, **kw) cls. (): =
就是一个能够创建类的对象,因为它继承了type
也正因为此,我们可以在这个类中去监控的创建过程
2、Hook实例属性
这里我们需要操作的属性是和
.(self, name) :无论访问存在还是不存在的属性都先访问该方法
.(self, name) :当不存在方法或者引发了异常时访问该方法
class C(): a = 'abc' def (self, *args, **): print(() is ) .(self, *args, **) def (self, name): print(() is ) namec = C()print c.() is c.() is () is
可以看到,访问已有属性a时,被调用,访问未定义的属性aa时先被调用,接着被调用
3、Hook类属性
描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 (), (), 和()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。
class Desc(): def (self, , owner):print(...) def (self, , value):print('...')class (): x = Desc()t = ()t....
- self: Desc的实例对象,其实就是的属性x
- : 的实例对象,其实就是t
- owner: 即谁拥有这些东西,当然是 这个类,它是最高统治者,其他的一些都是包含在它的内部或者由它生出来的
为了让描述符能够正常工作,它们必须定义在类的层次上。否则无法自动为你调用和方法。
而根据之前对类方法的说明,引用t.x的时候是否会先引用的方法呢?答案是会的,其实访问属性时在中真实的查找顺序是这样的:
1)(), 无条件调用
2)数据描述符(定义了或的描述符):由1)触发调用 (若人为的重载了该 () 方法,可能会导致无法调用描述符)
3)实例对象的字典
4)类的字典
5)非数据描述符(只定义了的描述符)
6)父类的字典
7)() 方法
4、使用修饰符来Hook类
def (cls, *args, **kw): = {} def (): if cls not in : [cls] = cls(*args, **kw) [cls] @ (): a = 1 def (self, x=0): self.x = x
我们使用方法把修饰为了一个单例模式,同时我们也在方法中实现了对实例过程的监控。
对方法进行Hook
1、修饰符来Hook方法
1)修饰不带参数的方法
def (func): def wrap(): print start func() print end wrap@ func(): pass
2)修饰带参数的方法
def (func): (*args,**kargv):print (*args,**kargv)print end wrap@ func(a,b): pass
3)使用带参数的修饰符来修饰方法
def (a,b): def (func):def wrap(*args,**kargv): print a func(*args,**kargv) print wrap @(1,2)def func(a,b): pass
其他Hook
1、Hook内建方法
#方法 = ..open = #方法 = __. =
上述操作使得代替了内置的open方法,故而我们可以使用我们自己的方法来监控后续对open方法的调用了
2、 Patch
from . speak(self): "!".speak = speak
实际上这是所有语言都会使用到的Hook技巧,往往在我们使用了第三方的包,希望在之上做一些扩展,但又不想改动原有的代码时使用
多说一句
上述提到了修饰符的操作,那么我们在使用修饰符时有一些小技巧需要了解
1、使用
防止使用修饰器后函数签名被改变
from (func): @wraps(func) def ():print %%func. func() @ foo(): pass
这样处理后,foo方法的签名与被修饰之前保持了一致,否则签名将会变成方法的签名
2、使用模块来做修饰器
from @ wrap(f,*args,**kw): print start f(*args,**kw) print end#这样wrap方法就变成了一个@ func(): print func
3、使用类做修饰器
class test(): (self,func): self._func = func (self): print start self._func() print end@ func(): print ()
实际应用中很少遇到可以使用一个类作为修饰器,但实际上只要一个类实现了方法,其就可以作为一个修饰器存在了,并且由于类的可操作性较方法更强大,所以类做修饰器也可以实现更丰富的特性。
小编就先聊到这里,今天交流的内容都是硬知识,普通的开发过程中也许并不能使用的上,但了解这些知识对于编程能力的提高很有帮助,也能够帮助你更深入的理解的机制。
实际应用
评论(0)