Python动态添加一个函数

时间:2010-05-07 14:52:42

标签: python metaprogramming

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

例如,我有一个班级:

 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 个答案:

答案 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

请注意,它也会修改现有实例的方法。

编辑:使用argskwargs

将装饰器的args列表修改为更好的版本

答案 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。 我刚刚发现这段代码很有趣,但我永远不会使用它。我不喜欢动态地改变对象本身。

这是最快的方式,也更容易理解。