如何在现有函数之前或之后添加代码?
例如,我有一个班级:
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()
有办法做到这一点吗?
答案 0 :(得分: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
请注意,它也会修改现有实例的方法。
编辑:使用args
和kwargs
答案 1 :(得分:10)
向函数添加功能的典型方法是使用decorator(使用the wraps function):
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"
这不需要元编程,但是您的示例可能会从您实际需要的内容中大大简化。也许如果你发布更多关于你的具体需求的细节,我们可以更好地建议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"
请注意,如果要添加将由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"
答案 2 :(得分:3)
上面有很多非常好的建议,但我没有看到的是通过调用传递函数。可能看起来像这样:
class A(object):
def test(self, deep=lambda self: self):
print "here"
deep(self)
def test2(self):
print "and here"
使用此:
>>> a = A()
>>> a.test()
here
>>> a.test(test2)
here
and here
答案 3 :(得分:3)
此答案允许您在不创建包装器的情况下修改原始功能。我找到了以下两种本机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
答案 4 :(得分:2)
为什么不使用继承?
class B(A):
def test(self):
super(B, self).test()
print "and here"
答案 5 :(得分:1)
复制并粘贴,享受!!!!!
#!/usr/bin/env python
def say(host, msg):
print '%s says %s' % (host.name, msg)
def funcToMethod(func, clas, method_name=None):
setattr(clas, method_name or func.__name__, func)
class transplant:
def __init__(self, method, host, method_name=None):
self.host = host
self.method = method
setattr(host, method_name or method.__name__, self)
def __call__(self, *args, **kwargs):
nargs = [self.host]
nargs.extend(args)
return apply(self.method, nargs, kwargs)
class Patient:
def __init__(self, name):
self.name = name
if __name__ == '__main__':
jimmy = Patient('Jimmy')
transplant(say, jimmy, 'say1')
funcToMethod(say, jimmy, 'say2')
jimmy.say1('Hello')
jimmy.say2(jimmy, 'Good Bye!')
答案 6 :(得分:0)
如果您的A类继承自object,您可以执行以下操作:
def test2():
print "test"
class A(object):
def test(self):
setattr(self, "test2", test2)
print self.test2
self.test2()
def main():
a = A()
a.test()
if __name__ == '__main__':
main()
此代码没有兴趣,您无法在添加的新方法中使用self。 我刚刚发现这段代码很有趣,但我永远不会使用它。我不喜欢动态地改变对象本身。
这是最快的方式,也更容易理解。