Python add to a function dynamically

前端 未结 7 1393
星月不相逢
星月不相逢 2020-12-08 04:53

how do i add code to an existing function, either before or after?

for example, i have a class:

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


        
相关标签:
7条回答
  • 2020-12-08 05:36

    The typical way to add functionality to a function is to use a decorator (using 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"
    

    Of course, it really depends on what you're trying to accomplish. I use decorators a lot, but if all I wanted to do was to print extra messages, I'd probably do something like

    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"
    

    This requires no meta-programming, but then again your example was probably greatly simplified from what you actually need to do. Perhaps if you post more detail about your specific needs, we can better advise what the Pythonic approach would be.

    EDIT: Since it seems like you want to call functions, you can have a list of functions instead of a list of messages. For example:

    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"
    

    Note that if you want to add functions that will be called by all instances of A, then you should make funcs a class field rather than an instance field, e.g.

    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"
    
    0 讨论(0)
  • 2020-12-08 05:40

    Why not use inheritance?

    class B(A):
        def test(self):
            super(B, self).test()
            print "and here"
    
    0 讨论(0)
  • 2020-12-08 05:41

    Copy and paste, enjoy!!!!!

    #!/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!') 
    
    0 讨论(0)
  • 2020-12-08 05:41

    If your class A inherits from object, you could do something like:

    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()
    

    This code has no interest and you can't use self in your new method you added. I just found this code fun, but I will never use this. I don't like to dynamicaly change the object itself.

    It's the fastest way, and more understandable.

    0 讨论(0)
  • 2020-12-08 05:51

    This answer allows you to modify the original function without creating a wrapper. I have found the following two native python types, which are useful to answer your question:

    types.FunctionType
    

    And

    types.CodeType
    

    This Code seems to do the job:

    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
    
    0 讨论(0)
  • 2020-12-08 05:59

    You can use a decorator to modify the function if you want. However, since it's not a decorator applied at the time of the initial definition of the function, you won't be able to use the @ syntactic sugar to apply it.

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

    Do note that it will modify the method for existing instances as well.

    EDIT: modified the args list for the decorator to the better version using args and kwargs

    0 讨论(0)
提交回复
热议问题