Python динамически добавляет функцию

как добавить код к существующей функции до или после?

например, у меня есть класс:

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

как отредактировать метапрограммирование класса wit, чтобы я сделал это

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

есть ли способ сделать это?

Ответы

Ответ 1

Вы можете использовать декоратор, чтобы изменить функцию, если хотите. Однако, поскольку это не декоратор, примененный во время первоначального определения функции, вы не сможете использовать синтаксический сахар @, чтобы применить его.

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

Обратите внимание, что он также изменит метод для существующих экземпляров.

EDIT: изменил список аргументов для декоратора в лучшую версию с помощью args и kwargs

Ответ 2

Типичным способом добавления функциональности к функции является использование decorator (используя функция обертки):

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-подход.

EDIT: поскольку кажется, что вы хотите вызывать функции, вы можете иметь список функций, а не список сообщений. Например:

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"

Ответ 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

Ответ 4

Почему бы не использовать наследование?

class B(A):
    def test(self):
        super(B, self).test()
        print "and here"

Ответ 5

Скопируйте и вставьте, наслаждайтесь!!!!!

#!/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

Этот ответ позволяет вам изменить исходную функцию без создания обертки. Я нашел следующие два родных типа 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 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

Ответ 7

Если ваш класс A наследуется от объекта, вы можете сделать что-то вроде:

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 в новом добавленном вами методе. Я просто нашел этот код веселым, но я никогда не буду использовать его. Мне не нравится динамически изменять сам объект.

Это самый быстрый способ и более понятный.