钩子python模块功能

时间:2016-03-02 21:21:50

标签: python

基本上我想做这样的事情: How can I hook a function in a python module?

但是我想在我自己的代码之后调用旧函数。

import whatever

oldfunc = whatever.this_is_a_function

def this_is_a_function(parameter):
    #my own code here
    # and call original function back
    oldfunc(parameter)

whatever.this_is_a_function = this_is_a_function

这可能吗?

我尝试了copy.copycopy.deepcopy原始功能,但它无效。

4 个答案:

答案 0 :(得分:6)

这样的东西?它避免使用全局变量,这通常是一件好事。

import whatever
import functools

def prefix_function(function, prefunction):
    @functools.wraps(function)
    def run(*args, **kwargs):
        prefunction(*args, **kwargs)
        return function(*args, **kwargs)
    return run

def this_is_a_function(parameter):
    pass # Your own code here that will be run before

whatever.this_is_a_function = prefix_function(
    whatever.this_is_a_function, this_is_a_function)

prefix_function是一个带有两个函数的函数:functionprefunction。它返回一个接受任何参数的函数,并使用相同的参数调用prefunction后跟functionprefix_function函数适用于任何可调用函数,因此您只需要为前缀代码编程一次,以便进行任何其他可能的挂钩操作。

@functools.wraps使得返回的包装函数的docstring和name是相同的。

如果您需要this_is_a_function使用不同于传递给它的参数调用旧whatever.this_is_a_function,您可以执行以下操作:

import whatever
import functools

def wrap_function(oldfunction, newfunction):
    @functools.wraps(function)
    def run(*args, **kwargs):
        return newfunction(oldfunction, *args, **kwargs)
    return run

def this_is_a_function(oldfunc, parameter):
    # Do some processing or something to customize the parameters to pass
    newparams = parameter * 2  # Example of a change to newparams
    return oldfunc(newparams)

whatever.this_is_a_function = wrap_function(
        whatever.this_is_a_function, this_is_a_function)

如果whatever是纯C模块,则存在一个问题,即首先改变其内部结构通常是不可能的(或非常困难)。

答案 1 :(得分:2)

所以,这是一个从time模块中修补time函数的示例。

import time

old_time = time.time

def time():
    print('It is today... but more specifically the time is:')
    return old_time()

time.time = time

print time.time()
# Output:
# It is today... but more specifically the time is:
# 1456954003.2

但是,如果您尝试对C代码执行此操作,则很可能会收到类似cannot overwrite attribute的错误。在这种情况下,您可能希望子类化C模块。

您可能需要查看this question

答案 2 :(得分:2)

这是吹捧我超级简单的胡克的最佳时机

def hook(hookfunc, oldfunc):
    def foo(*args, **kwargs):
        hookfunc(*args, **kwargs)
        return oldfunc(*args, **kwargs)
    return foo

难以置信的简单。它将返回一个首先运行所需钩子函数的函数(具有相同的参数,请注意),然后运行您正在挂钩的原始函数并返回该原始值。这也可以覆盖类方法。假设我们在类中有静态方法。

class Foo:
    @staticmethod
    def bar(data):
        for datum in data:
            print(datum, end="") # assuming python3 for this
        print()

但是我们想在打印出元素之前打印数据的长度

def myNewFunction(data):
    print("The length is {}.".format(len(data)))

现在我们简单地挂钩函数

Foo.bar(["a", "b", "c"])
# => a b c
Foo.bar = hook(Foo.bar, myNewFunction)
Foo.bar(["x", "y", "z"])
# => The length is 3.
# => x y z 

答案 3 :(得分:0)

实际上,您可以替换目标函数func_code。以下示例

# a normal function
def old_func():
    print "i am old"

# a class method
class A(object):
    def old_method(self):
        print "i am old_method"

# a closure function
def make_closure(freevar1, freevar2):
    def wrapper():
        print "i am old_clofunc, freevars:", freevar1, freevar2
    return wrapper
old_clofunc = make_closure('fv1', 'fv2')

# ===============================================

# the new function
def new_func(*args):
    print "i am new, args:", args
# the new closure function
def make_closure2(freevar1, freevar2):
    def wrapper():
        print "i am new_clofunc, freevars:", freevar1, freevar2
    return wrapper
new_clofunc = make_closure2('fv1', 'fv2')

# ===============================================

# hook normal function
old_func.func_code = new_func.func_code
# hook class method
A.old_method.im_func.func_code = new_func.func_code
# hook closure function
# Note: the closure function's `co_freevars` count should be equal
old_clofunc.func_code = new_clofunc.func_code

# ===============================================

# call the old
old_func()
A().old_method()
old_clofunc()

输出:

i am new, args: ()
i am new, args: (<__main__.A object at 0x0000000004A5AC50>,)
i am new_clofunc, freevars: fv1 fv2