2010-05-07 19 views
15

Wie füge ich Code zu einer vorhandenen Funktion, vor oder nach?Python hinzufügen zu einer Funktion dynamisch

zum Beispiel habe ich eine Klasse:

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

, wie ich die Klasse Witz metaprogramming bearbeite, so dass ich dies tun

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

     print "and here" 

vielleicht in gewisser Weise eine andere Funktion von Anhängen zu testen?

hinzufügen eine andere Funktion wie

def test2(self): 
     print "and here" 

und die

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

zu

Original ändern, ist es eine Möglichkeit, dies zu tun?

Antwort

18

Sie können einen Dekorator verwenden, um die Funktion zu ändern, wenn Sie möchten. Da es sich bei der ersten Definition der Funktion jedoch nicht um einen Decorator handelt, können Sie den Syntax-Zucker @ nicht verwenden, um ihn anzuwenden.

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

Beachten Sie, dass es auch die Methode für vorhandene Instanzen ändert.

EDIT: modifiziert, um die args-Liste für den Dekorateur zum besser Version args und kwargs

+0

danke, das ist, was ich brauchte – Timmy

2

Warum Vererbung nicht verwenden?

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

die kurz ist ich musste dynamisch Eltern zu einer Klasse hinzufügen, also kann ich das Super leicht nicht nennen, ohne zu tun, was die Frage in erster Linie – Timmy

+0

Angemessene genug fragt. Dekorateure scheinen definitiv der richtige Weg zu sein. –

10

Die typische Art und Weise Funktionalität zu einer Funktion hinzuzufügen, ist ein decorator (mit the wraps function) zu verwenden:

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" 

Natürlich ist es hängt wirklich davon ab, was Sie erreichen wollen. Ich benutze Dekorateure viel, aber wenn alles was ich wollte war drucken zusätzliche Nachrichten tun, ich wahrscheinlich wie etwas tun würde

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" 

Dies erfordert keine Meta-Programmierung, aber dann wieder Ihr Beispiel wurde vermutlich stark vereinfacht aus was du eigentlich tun musst. Vielleicht können Sie, wenn Sie detaillierter auf Ihre spezifischen Bedürfnisse eingehen, besser beraten, was der pythonische Ansatz wäre.

EDIT: Da es so aussieht, als ob Sie Funktionen aufrufen möchten, können Sie eine Liste von Funktionen anstelle einer Liste von Nachrichten haben. Zum Beispiel:

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" 

Beachten Sie, dass, wenn Sie Funktionen hinzufügen möchten, die von allen Instanzen A aufgerufen wird, dann sollten Sie funcs ein Klassenfeld machen, anstatt eine Instanz Feld, z.B.

0

Verwendung Wenn Ihre Klasse A von Objekt erbt, Sie so etwas wie tun könnte:

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

Dieser Code hat kein Interesse und Sie können in Ihrer neuen Methode, die Sie hinzugefügt haben, kein Selbst verwenden. Ich fand gerade diesen Code Spaß, aber ich werde nie diesen verwenden. Ich mag es nicht, das Objekt selbst dynamisch zu verändern.

Es ist der schnellste Weg und verständlicher.

4

Es gibt eine Menge wirklich gute Vorschläge oben, aber eins, das ich nicht sah, ging in einer Funktion mit dem Anruf über. Könnte wie folgt aussehen:

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

diese verwenden:

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

Kopieren und Einfügen, genießen !!!!!

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

Mit dieser Antwort können Sie die ursprüngliche Funktion ändern, ohne einen Wrapper zu erstellen. Ich habe die folgenden zwei nativen Python-Typen gefunden, die nützlich sind, um Ihre Frage zu beantworten:

types.FunctionType 

Und

types.CodeType 

Dieser Kodex, den Job zu tun scheint:

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

Ich finde jedoch aggivated, dass meine Lösung nicht für mehr als eine Iteration einer Funktionsmodifikationsschleife funktioniert. dill.source.getsource funktioniert nur mit der ursprünglichen Version der Funktion und kann nach dem Ändern der Funktion nicht erneut verwendet werden, um den Quellcode zu erhalten. Es scheint, dass Python nur den einmaligen ursprünglich kompilierten Quellcode verfolgt und NICHT die Quellcodeänderungen in einer Funktion verfolgt. Im Prinzip könnten Sie die kompilierte Version der Funktion nehmen und dann den Python-Code rekonstruieren ... aber das scheint albern zu sein. –

Verwandte Themen