2010-05-07 15 views
15

¿cómo agrego el código a una función existente, ya sea antes o después?Python agregar a una función de forma dinámica

por ejemplo, tengo una clase:

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

¿Cómo edito el ingenio metaprogramming clase para que puedo hacer esto

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

     print "and here" 

quizás alguna forma de añadir otra función a prueba?

añadir otra función como

def test2(self): 
     print "and here" 

y cambiar el original a

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

hay una manera de hacer esto?

Respuesta

18

Puede usar un decorador para modificar la función si lo desea. Sin embargo, dado que no se trata de un decorador aplicado en el momento de la definición inicial de la función, no podrá usar el azúcar sintáctico @ para aplicarlo.

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

Tenga en cuenta que también modificará el método para las instancias existentes.

EDITAR: modificó la lista de argumentos para el decorador a la mejor versión usando args y kwargs

+0

gracias, esto es lo que necesitaba – Timmy

2

¿Por qué no utilizar la herencia?

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

corto es Tuve que agregar dinámicamente a los padres a una clase, por lo que no puedo llamar al súper fácilmente, sin hacer lo que la pregunta está haciendo, en primer lugar, – Timmy

+0

. Los decoradores definitivamente parecen ser el camino a seguir en ese momento. –

10

La forma típica de agregar funcionalidad a una función es utilizar un decorator (usando 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" 

Por supuesto, lo que realmente depende de lo que estamos tratando de lograr. Yo uso decoradores mucho, pero si todo lo que quería hacer era imprimir mensajes adicionales, probablemente haría algo así como

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" 

Esto no requiere meta-programación, pero por otra parte su ejemplo fue probablemente muy simplificado de lo que realmente necesitas hacer Quizás si publica más detalles sobre sus necesidades específicas, podemos aconsejar mejor cuál sería el enfoque Pythonic.

EDITAR: Como parece que quiere llamar a funciones, puede tener una lista de funciones en lugar de una lista de mensajes. Por ejemplo:

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" 

Tenga en cuenta que si desea agregar funciones que serán llamados por todas las instancias de A, entonces usted debe hacer funcs un campo de clase en lugar de un campo de instancia, por ejemplo,

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

Si la clase A hereda de objeto, se podría hacer algo como:

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

Este código no tiene interés y no puedes usarlo en tu nuevo método que hayas agregado. Acabo de encontrar este código divertido, pero nunca lo usaré. No me gusta cambiar dinámicamente el objeto en sí.

Es la manera más rápida y más comprensible.

4

Aquí hay muchas sugerencias realmente buenas, pero una que no vi fue pasar una función con la llamada. Podría ser algo como esto:

class A(object): 
    def test(self, deep=lambda self: self): 
     print "here" 
     deep(self) 
def test2(self): 
    print "and here" 

El uso de este:

>>> a = A() 
>>> a.test() 
here 
>>> a.test(test2) 
here 
and here 
1

Copiar y pegar, disfrutar !!!!!

#!/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!') 
1

Esta respuesta le permite modificar la función original sin crear un envoltorio. He encontrado los siguientes dos tipos de pitón nativas, que son útiles para responder a su pregunta:

types.FunctionType 

Y

types.CodeType 

Este Código parece hacer el trabajo:

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

Sin embargo, me encuentro agredido que mi solución no funciona para más de una iteración de un ciclo de modificación de función. dill.source.getsource solo funciona en la versión original de la función, y después de modificar la función, no se puede usar nuevamente para obtener el código fuente. Parecería que Python solo realiza un seguimiento del código fuente original compilado originalmente, y NO realiza un seguimiento de los cambios del código fuente a una función. En principio, podrías tomar la versión compilada de la función y luego reconstruir el código python ... pero parece una tontería. –

Cuestiones relacionadas