什么是Hook,就是在一个已有的方法上加入一些钩子,使得在该方法执行前或执行后另在做一些额外的处理,那么Hook技巧有什么作用以及我们为什么需要使用它呢,事实上如果一个项目在设计架构时考虑的足够充分,模块抽象的足够合理,设计之初为以后的扩展预留了足够的接口,那么我们完全可以不需要Hook技巧。但恰恰架构人员在项目设计之初往往没办法想的足够的深远,使得后续在扩展时深圳面临重构的痛苦,这时Hook技巧似乎可以为我们带来一记缓兵之计,通过对旧的架构进行加钩子来满足新的扩展需求。
下面我们就来看看如果进行Hook处理,我们按照Hook的对象的层级来逐一介绍
对类进行Hook
也就是说我们得钩子需要监控到类的创建等操作,然后在此之前或之后做我们希望的操作
1、Hook类的创建
你可以在写一个类的时候为其添加__metaclass__属性
class Foo(Bar): __metaclass__ = something…
Python创建类的过程是这样的:
Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
所以我们需要在给__metaclass__属性的值是一个能够创建一个类的东西,即一个继承type的类。
比如:
复制代码 代码如下:
class Singleton(type): def__init__(cls, name, bases, dict): super(Singleton, cls).__init__(name, bases, dict) cls._instance = None def__call__(cls, *args, **kw): if cls._instance is None: cls._instance = super(Singleton, cls).__call__(*args, **kw) return cls._instanceclass MyClass(object): __metaclass__ = Singleton
Singleton就是一个能够创建类的对象,因为它继承了type
也正因为此,我们可以在Singleton这个类中去监控MyClass的创建过程
2、Hook实例属性
这里我们需要操作的属性是__getattribute__和__getattr__
object.__getattribute__(self, name) :无论访问存在还是不存在的属性都先访问该方法
object.__getattr__(self, name) :当不存在__getattribute__方法或者引发了AttributeError异常时访问该方法
复制代码 代码如下:
class C(object): a = 'abc' def __getattribute__(self, *args, **kwargs): print(__getattribute__() is called) return object.__getattribute__(self, *args, **kwargs) def __getattr__(self, name): print(__getattr__() is called) return namec = C()print c.a__getattribute__() is calledabcprint c.aa__getattribute__() is called__getattr__() is calledaa
可以看到,访问已有属性a时,__getattribute__被调用,访问未定义的属性aa时__getattribute__先被调用,接着__getattr__被调用
3、Hook类属性
python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 __get__(), __set__(), 和__delete__()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。
复制代码 代码如下:
class Desc(object): def __get__(self, instance, owner):print(__get__...) def __set__(self, instance, value):print('__set__...')class TestDesc(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、使用修饰符来Hook类
复制代码 代码如下:
def singleton(cls, *args, **kw): instances = {} def _singleton(): if cls not in instances: instances[cls] = cls(*args, **kw) return instances[cls] return _singleton@singletonclass MyClass(object): a = 1 def __init__(self, x=0): self.x = x
我们使用singleton方法把MyClass修饰为了一个单例模式,同时我们也在singleton方法中实现了对MyClass实例过程的监控。
对方法进行Hook
1、修饰符来Hook方法
1)修饰不带参数的方法
复制代码 代码如下:
def something(func): def wrap(): print start func() print end return wrap@somethingdef func(): pass
2)修饰带参数的方法
复制代码 代码如下:
def something(func): defwrap(*args,**kargv):print startfunc(*args,**kargv)print end return wrap@somethingdef func(a,b): pass
3)使用带参数的修饰符来修饰方法
复制代码 代码如下:
def something(a,b): def new_func(func):def wrap(*args,**kargv): print a func(*args,**kargv) print breturn wrap return new_func@something(1,2)def func(a,b): pass
其他Hook
1、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、Monkey Patch
复制代码 代码如下:
from SomeOtherProduct.SomeModule import SomeClassdef speak(self): return "ookookeeeeeeeee!"SomeClass.speak = speak
实际上这是所有语言都会使用到的Hook技巧,往往在我们使用了第三方的包,希望在之上做一些扩展,但又不想改动原有的代码时使用
多说一句
上述提到了修饰符的操作,那么我们在使用修饰符时有一些小技巧需要了解
1、使用functools
防止使用修饰器后函数签名被改变
复制代码 代码如下:from functools import wrapsdef my_dec(func): @wraps(func) def wrapped():print %siscalled%func.__name__return func() return wrapped@my_decdef foo(): pass
这样处理后,foo方法的签名与被修饰之前保持了一致,否则签名将会变成my_dec方法的签名
2、使用decorator模块来做修饰器
复制代码 代码如下:
from decorator import decorator@decoratordef wrap(f,*args,**kw): print start f(*args,**kw) print end#这样wrap方法就变成了一个decorator@wrapdef func(): print func
3、使用类做修饰器
复制代码 代码如下:class test(object): def__init__(self,func): self._func = func def__call__(self): print start self._func() print end@testdef func(): print funcfunc()startfuncend
实际应用中很少遇到可以使用一个类作为修饰器,但实际上只要一个类实现了__call__方法,其就可以作为一个修饰器存在了,并且由于类的可操作性较方法更强大,所以类做修饰器也可以实现更丰富的特性。
下面留个示例深入理解
# -*- coding: utf-8 -*- # import pythoncom import pyHook def onMouseEvent(event): # 监听鼠标事件 print "MessageName:",event.MessageName print "Message:", event.Message print "Time:", event.Time print "Window:", event.Window print "WindowName:", event.WindowName print "Position:", event.Position print "Wheel:", event.Wheel print "Injected:", event.Injected print "---" # 返回 True 以便将事件传给其它处理程序 # 注意,这儿如果返回 False ,则鼠标事件将被全部拦截 # 也就是说你的鼠标看起来会僵在那儿,似乎失去响应了 return True def onKeyboardEvent(event): # 监听键盘事件 print "MessageName:", event.MessageName print "Message:", event.Message print "Time:", event.Time print "Window:", event.Window print "WindowName:", event.WindowName print "Ascii:", event.Ascii, chr(event.Ascii) print "Key:", event.Key print "KeyID:", event.KeyID print "ScanCode:", event.ScanCode print "Extended:", event.Extended print "Injected:", event.Injected print "Alt", event.Alt print "Transition", event.Transition print "---" # 同鼠标事件监听函数的返回值 return True def main(): # 创建一个“钩子”管理对象 hm = pyHook.HookManager() # 监听所有键盘事件 hm.KeyDown = onKeyboardEvent # 设置键盘“钩子” hm.HookKeyboard() # 监听所有鼠标事件 hm.MouseAll = onMouseEvent # 设置鼠标“钩子” hm.HookMouse() # 进入循环,如不手动关闭,程序将一直处于监听状态 pythoncom.PumpMessages() if __name__ == "__main__": main()
#将test.py变为test.exe #Get py2exe from http://www.py2exe.org/ from distutils.core import setup import py2exe setup(console=['test.py']) #cmd下执行:python setup.py py2exe,在dist目录下有exe和必备dll
#隐藏控制台,让其一闪而过 import ctypes whnd = ctypes.windll.kernel32.GetConsoleWindow() if whnd != 0: ctypes.windll.user32.ShowWindow(whnd, 0) ctypes.windll.kernel32.CloseHandle(whnd)
小编就先聊到这里,今天交流的内容都是硬知识,普通的开发过程中也许并不能使用的上,但了解这些知识对于编程能力的提高很有帮助,也能够帮助你更深入的理解Python的机制。也希望大家多多支持。
Python,Hook
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 雨林唱片《赏》新曲+精选集SACD版[ISO][2.3G]
- 罗大佑与OK男女合唱团.1995-再会吧!素兰【音乐工厂】【WAV+CUE】
- 草蜢.1993-宝贝对不起(国)【宝丽金】【WAV+CUE】
- 杨培安.2009-抒·情(EP)【擎天娱乐】【WAV+CUE】
- 周慧敏《EndlessDream》[WAV+CUE]
- 彭芳《纯色角3》2007[WAV+CUE]
- 江志丰2008-今生为你[豪记][WAV+CUE]
- 罗大佑1994《恋曲2000》音乐工厂[WAV+CUE][1G]
- 群星《一首歌一个故事》赵英俊某些作品重唱企划[FLAC分轨][1G]
- 群星《网易云英文歌曲播放量TOP100》[MP3][1G]
- 方大同.2024-梦想家TheDreamer【赋音乐】【FLAC分轨】
- 李慧珍.2007-爱死了【华谊兄弟】【WAV+CUE】
- 王大文.2019-国际太空站【环球】【FLAC分轨】
- 群星《2022超好听的十倍音质网络歌曲(163)》U盘音乐[WAV分轨][1.1G]
- 童丽《啼笑姻缘》头版限量编号24K金碟[低速原抓WAV+CUE][1.1G]