什么是Hook,就是给一个已有的方法添加一些hook,从而在方法执行之前或者之后做一些额外的处理,那么Hook技巧的作用是什么,为什么我们需要使用它吗?其实,如果一个项目在设计架构的时候考虑的足够多,模块抽象的足够合理,在设计之初就预留了足够多的接口供以后的扩展使用,那么我们根本就不需要Hook技术。然而,恰恰是建筑师在项目设计之初往往想得不够远,使得深圳在后续扩建中面临着重构之痛。这时,Hook技术似乎给我们带来了缓兵之计。添加钩子以满足新的扩展需求。接下来我们看一下如何进行Hook处理。我们将根据Hook对象的层级一一介绍类的Hooking。也就是说,我们的钩子需要监听类的创建等操作,然后在这之前或者之后做我们想做的事情。操作1.创建一个Hook类可以添加__metaclass__属性classFoo(Bar):__metaclass__=something...Python中创建类的过程是这样的:Foo是否有属性__metaclass__??如果是这样,Python通过__metaclass__在内存中创建一个名为Foo的类。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试像以前一样做。如果Python在任何父类中都找不到__metaclass__,它会在模块层次结构中查找__metaclass__并尝试执行相同的操作。如果仍未找到__metaclass__,Python将使用内置类型创建类对象。所以我们需要给__metaclass__属性的值赋予一个可以创建类的东西,也就是继承类型的类。例如:classSingleton(type):def__init__(cls,name,bases,dict):super(Singleton,cls).__init__(name,bases,dict)cls._instance=Nonedef__call__(cls,*args,**kw):ifcls._instanceisNone:cls._instance=super(Singleton,cls).__call__(*args,**kw)returncls._instanceclassMyClass(object):__metaclass__=SingletonSingleton是一个可以创建类的对象,因为它,我们可以在Singleton类中监控MyClass的创建过程。2.Hook实例属性这里我们需要操作的属性是__getattribute__和__getattr__object.__getattribute__(self,name):不管是否存在访问仍然不存在的属性先访问本方法object.__getattr__(self,name):访问当没有__getattribute__方法或引发AttributeError异常时使用此方法classC(object):a='abc'def__getattribute__(self,*args,**kwargs):print(__getattribute__()iscalled)returnobject.__getattribute__(self,*args,**kwargs)def__getattr__(self,name):print(__getattr__()iscalled)returnnamec=C()printc.a__getattribute__()iscalledabcprintc.aa__getattribute__()iscalled__getattr__()是calledaa可以看到在访问已有的属性a时,调用了__getattribute__,访问挂起定义属性aa时,先调用__getattribute__,再调用__getattr__。3、Hook类属性python描述符是“绑定行为”的对象属性。在描述符协议中,它可以通过方法覆盖属性。访问方法有__get__()、__set__()和__delete__()。如果在对象上定义了这些方法中的任何一个,则该对象是一个描述符。类Desc(对象):def__get__(self,instance,owner):print(__get__...)def__set__(self,instance,value):print('__set__...')classTestDesc(object):x=desc()t=TestDesc()t.x__get__...-self:Desc的实例对象其实就是TestDesc的属性??x-instance:TestDesc的实例对象其实就是t-owner:谁拥有这些东西,当然TestDesc类是最终的统治者,其他一些包含在其中或由它生成。为了使描述符正常工作,它们必须在类级别定义。否则Python无法自动为您调用__get__和__set__方法。根据前面类方法的描述,在引用t.x的时候会先引用TestDesc的__getattribute__方法吗?答案是肯定的。其实python中访问属性时真正的查找顺序是这样的:1)__getattribute__(),无条件调用2)数据描述符(定义__set__或__delete__的描述符):通过1)触发调用(如果__getattribute__()方法是人为调用的)重载,描述符可能不会被调用)3)实例对象的字典4)类的字典5)非数据描述符(只定义了__get__描述符)6)父类字典7)__getattr__()方法4.使用修饰符来Hookclassdefsingleton(cls,*args,**kw):instances={}def_singleton():如果cls不在实例中:instances[cls]=cls(*args,**kw)returninstances[cls]return_singleton@singletonclassMyClass(object):a=1def__init__(self,x=0):self.x=x我们使用singleton方法将MyClass修改为单例模式,同时我们也实现了对MyClass实例进程的监控在单例方法中。Hookmethod1,修改Hook方法1)修改无参数方法defsomething(func):defwrap():printstartfunc()printendreturnwrap@somethingdeffunc():pass2)修改有参数方法methoddefsomething(func):defwrap(*args,**kargv):printstartfunc(*args,**kargv)printendreturnwrap@somethingdeffunc(a,b):pass3)使用带参数的修饰符来修改方法defsomething(a,b):defnew_func(func):defwrap(*args,**kargv):打印一个func(*args,**kargv)printbreturnwrapreturnnew_func@something(1,2)deffunc(a,b):传递其他Hook1,Hook内置方法#Hookopen方法real_open=__builtins__.open__builtin__.open=my_open#Hookimport方法real_importer=__import____builtins__.__import__=my_importer以上操作让my_open替换了python内置的open方法,所以我们可以使用我们自己的my_open方法来监控对open方法的后续调用2.MonkeyPatchfromSomeOtherProduct.SomeModuleimportSomeClassdefspeak(self):return"ookookeeeeeeeeee!!"SomeClass.speak=speak其实所有语言都使用Hook技术,当我们使用第三方包,希望在其之上做一些扩展,又不想改动原有代码时,往往会用到Hook技术。再说一下上面提到的修饰符的操作,那么我们在使用修饰符的时候有一些小技巧需要知道1.使用functools防止使用装饰器fromfunctoolsimportwraps后改变函数签名defmy_dec(func):@wraps(func)defwrapped():print%siscalled%func.__name__returnfunc()returnwrapped@my_decdeffoo():pass这样处理后,foo方法的签名保持不变和修改之前一样,否则签名会变成my_dec方法的签名2.使用装饰器模块作为装饰器fromdecoratorimportdecorator@decoratordefwrap(f,*args,**kw):printstartf(*args,**kw)printend#这样wrap方法就变成了一个装饰器@wrapdeffunc():printfunc3,使用一个类作为装饰器classtest(object):def__init__(self,func):self._func=funcdef__call__(self):printstartself._func()printend@testdeffunc():printfuncfunc()startfuncend在实际应用中很少用一个类作为装饰器,但其实只要一个类实现__call__方法,它可以作为一个装饰器存在,并且由于类的可操作性比方法强大,类作为装饰器也可以实现有更丰富的功能。我先说到这里。今天交流的内容是硬知识,普通的开发过程中你可能用不到,但是了解这些知识对于提高你的编程能力是很有帮助的,也可以帮助你更深入的理解Python的机制。
