Python 动态添加函数
我该怎么在一个已经存在的函数里添加代码呢?可以是在函数之前或者之后。
比如说,我有一个类:
class A(object):
def test(self):
print "here"
我该怎么用元编程来编辑这个类,让我能做到这个呢?
class A(object):
def test(self):
print "here"
print "and here"
也许可以用某种方法把另一个函数加到测试里?
添加一个像这样的函数:
def test2(self):
print "and here"
然后把原来的改成:
class A(object):
def test(self):
print "here"
self.test2()
有没有办法做到这一点呢?
7 个回答
6
这个回答让你可以在不创建额外包装的情况下修改原来的函数。我发现了以下两种原生的 Python 类型,它们对回答你的问题很有帮助:
types.FunctionType
还有
types.CodeType
这段代码似乎可以完成这个任务:
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
待办事项:
修改这个回答,让 dill.source.getsource
能够获取正确的新源代码。
10
通常,给一个函数增加功能的方法是使用装饰器(配合wraps函数)。
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"
当然,这其实要看你想要实现什么。如果我只是想打印一些额外的信息,我可能会这样做:
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"
这样做不需要复杂的编程,但你的例子可能是大大简化过的,跟你实际需要的情况不太一样。也许如果你能提供更多关于你具体需求的细节,我们可以更好地建议你用Python的方式来解决。
编辑:看起来你想要调用函数,你可以用一个函数列表来代替消息列表。例如:
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"
注意,如果你想添加的函数是所有类的实例都要调用的,那么你应该把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"
27
如果你想的话,可以用一个装饰器来修改这个函数。不过,因为这个装饰器不是在函数最开始定义的时候就应用的,所以你不能用 @
这种简写方式来使用它。
>>> 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
请注意,这样做会影响到已经存在的实例的方法。
编辑:把装饰器的参数列表改成了更好的版本,使用了 args
和 kwargs
。