Python动态添加一个函数

Tim*_*mmy 17 python metaprogramming

如何在现有函数之前或之后添加代码?

例如,我有一个班级:

 class A(object):
     def test(self):
         print "here"
Run Code Online (Sandbox Code Playgroud)

我如何编辑类机智元编程,以便我这样做

 class A(object):
     def test(self):
         print "here"

         print "and here"
Run Code Online (Sandbox Code Playgroud)

也许某种方式附加另一个功能来测试?

添加另一个功能,如

 def test2(self):
      print "and here"
Run Code Online (Sandbox Code Playgroud)

并将原始更改为

 class A(object):
     def test(self):
         print "here"
         self.test2()
Run Code Online (Sandbox Code Playgroud)

有没有办法做到这一点?

Dan*_*olo 22

如果需要,可以使用装饰器修改函数.但是,由于它不是在函数的初始定义时应用的装饰器,因此您将无法使用@语法糖来应用它.

>>> class A(object):
...     def test(self):
...         print "orig"
...
>>> first_a = A()
>>> first_a.test()
orig
>>> def decorated_test(fn):
...     def new_test(*args, **kwargs):
...         fn(*args, **kwargs)
...         print "new"
...     return new_test
...
>>> A.test = decorated_test(A.test)
>>> new_a = A()
>>> new_a.test()
orig
new
>>> first_a.test()
orig
new
Run Code Online (Sandbox Code Playgroud)

请注意,它也将修改现有实例的方法.

编辑:使用和修改装饰器的args列表到更好的版本argskwargs


Eli*_*ght 10

向函数添加功能的典型方法是使用装饰器(使用包装函数):

from functools import wraps

def add_message(func):
    @wraps
    def with_additional_message(*args, **kwargs)
        try:
            return func(*args, **kwargs)
        finally:
            print "and here"
    return with_additional_message

class A:
    @add_message
    def test(self):
        print "here"
Run Code Online (Sandbox Code Playgroud)

当然,这实际上取决于你想要完成的事情.我经常使用装饰器,但如果我想做的就是打印额外的消息,我可能会做类似的事情

class A:
    def __init__(self):
        self.messages = ["here"]

    def test(self):
        for message in self.messages:
            print message

a = A()
a.test()    # prints "here"

a.messages.append("and here")
a.test()    # prints "here" then "and here"
Run Code Online (Sandbox Code Playgroud)

这不需要元编程,但是你的例子可能会从你真正需要做的事情中大大简化.也许如果您发布有关您的特定需求的更多细节,我们可以更好地建议Pythonic方法.

编辑:因为看起来你想要调用函数,你可以有一个函数列表而不是消息列表.例如:

class A:
    def __init__(self):
        self.funcs = []

    def test(self):
        print "here"
        for func in self.funcs:
            func()

def test2():
    print "and here"

a = A()
a.funcs.append(test2)
a.test()    # prints "here" then "and here"
Run Code Online (Sandbox Code Playgroud)

请注意,如果要添加将由所有实例调用的函数,A则应创建funcs类字段而不是实例字段,例如

class A:
    funcs = []
    def test(self):
        print "here"
        for func in self.funcs:
            func()

def test2():
    print "and here"

A.funcs.append(test2)

a = A()
a.test()    # print "here" then "and here"
Run Code Online (Sandbox Code Playgroud)


D A*_*ams 5

此答案允许您在不创建包装器的情况下修改原始函数。我发现了以下两种原生 python 类型,它们对回答您的问题很有用:

types.FunctionType
Run Code Online (Sandbox Code Playgroud)

types.CodeType
Run Code Online (Sandbox Code Playgroud)

这段代码似乎可以完成这项工作:

import inspect
import copy
import types
import dill
import dill.source


#Define a function we want to modify:
def test():
    print "Here"

#Run the function to check output
print '\n\nRunning Function...'
test()
#>>> Here

#Get the source code for the test function:
testSource = dill.source.getsource(test)
print '\n\ntestSource:'
print testSource


#Take the inner part of the source code and modify it how we want:
newtestinnersource = ''
testSourceLines = testSource.split('\n')
linenumber = 0 
for line in testSourceLines:
    if (linenumber > 0):
        if (len(line[4:]) > 0):
            newtestinnersource += line[4:] + '\n'
    linenumber += 1
newtestinnersource += 'print "Here2"'
print '\n\nnewtestinnersource'
print newtestinnersource


#Re-assign the function's code to be a compiled version of the `innersource`
code_obj = compile(newtestinnersource, '<string>', 'exec')
test.__code__ = copy.deepcopy(code_obj)
print '\n\nRunning Modified Function...'
test() #<- NOW HAS MODIFIED SOURCE CODE, AND PERFORMS NEW TASK
#>>>Here
#>>>Here2
Run Code Online (Sandbox Code Playgroud)

TODO: 更改此答案以dill.source.getsource获得正确的新源代码。