2013-12-20 12 views
6

Angenommen, die Schaffung habe ich diese Funktion:eine Python-Funktion zur Laufzeit mit dem angegebenen Argumente Namen

def f(x,y): 
    return x+y 

Wenn ich inspect.getargspec(f).args verwende ich ['x','y'] als Ergebnis erhalten. Groß.

Jetzt nehme ich eine andere Funktion g(a,b) zur Laufzeit erstellt werden soll, wo ich weiß nicht, die Argumentnamen a und b bis zur Laufzeit:

def g(a,b): 
    return f(a,b) 

Gibt es eine Möglichkeit, dies zu tun? Lambdas sind fast richtig, außer dass ich zur Kompilierungszeit nur Argumentnamen zuweisen kann.

g = lambda *p: f(*p) 

Irgendwie möchte ich die Funktion zur Laufzeit dynamisch auf einer Liste L (zB L=['a','b']) basiert erstellen, so dass inspect.getargspec(g).args == L.

+0

nicht sicher, ob ich die Frage verstanden. Ich nehme an, Sie könnten das 'Lambda' mit einer 'eval'-Anweisung erstellen ... Können Sie ein Beispiel für eine Funktion geben, die zur Laufzeit erstellt wurde? –

+0

Sie können 'FunctionType' und' CodeType' verwenden, um das Objekt zu konstruieren, aber ich bin mir nicht sicher, ob ich den Punkt sehe. – DSM

+2

Das klingt wie ein XY-Problem - In der gleichen war wie dynamisch benannt _Variablen_ normalerweise bedeutet der Fragesteller will eine Liste, das fühlt sich an wie der falsche Weg, um ein Problem zu lösen – Eric

Antwort

3

Hier ist ein etwas hacky Weg, es zu tun Das erstellt zuerst eine neue Funktion aus einer bestehenden mit der Änderung und ersetzt dann den Code des Originals damit. Es ist in erster Linie, weil der Aufruf types.CodeType() so viele Argumente hat. Die Python 3-Version (nicht abgebildet), die ich ebenfalls implementiert habe, ist etwas anders, da eine Anzahl der function.func_code-Attribute umbenannt wurde und die Aufrufsequenz von types.CodeType() leicht geändert wurde.

Ich habe die Idee von this answer von @Aaronasterling (wer sagt, er hat die Idee von Michael Foord Voidspace Blog mit dem Titel Selfless Python).Es könnte leicht zu einem Dekorateur gemacht werden, aber ich sehe das nicht als hilfreich an, basierend auf dem, was Sie uns über den beabsichtigten Gebrauch gesagt haben.

import types 

def change_func_args(function, new_args): 
    """ Create a new function with its arguments renamed to new_args. """ 
    code_obj = function.func_code 
    assert(0 <= len(new_args) <= code_obj.co_argcount) 
    # the arguments are just the first co_argcount co_varnames 
    # replace them with the new argument names in new_args 
    new_varnames = tuple(list(new_args[:code_obj.co_argcount]) + 
         list(code_obj.co_varnames[code_obj.co_argcount:])) 
    # type help(types.CodeType) at the interpreter prompt for information 
    new_code_obj = types.CodeType(code_obj.co_argcount, 
            code_obj.co_nlocals, 
            code_obj.co_stacksize, 
            code_obj.co_flags, 
            code_obj.co_code, 
            code_obj.co_consts, 
            code_obj.co_names, 
            new_varnames, 
            code_obj.co_filename, 
            code_obj.co_name, 
            code_obj.co_firstlineno, 
            code_obj.co_lnotab, 
            code_obj.co_freevars, 
            code_obj.co_cellvars) 
    modified = types.FunctionType(new_code_obj, function.func_globals) 
    function.__code__ = modified.__code__ # replace code portion of original 

if __name__ == '__main__': 

    import inspect 

    def f(x, y): 
     return x+y 

    def g(a, b): 
     return f(a, b) 

    print('Before:') 
    print('inspect.getargspec(g).args: {}'.format(inspect.getargspec(g).args)) 
    print('g(1, 2): {}'.format(g(1, 2))) 

    change_func_args(g, ['p', 'q']) 

    print('') 
    print('After:') 
    print('inspect.getargspec(g).args: {}'.format(inspect.getargspec(g).args)) 
    print('g(1, 2): {}'.format(g(1, 2))) 

Ausgang:

Before: 
inspect.getargspec(g).args: ['a', 'b'] 
g(1, 2): 3 

After: 
inspect.getargspec(g).args: ['p', 'q'] 
g(1, 2): 3 
1

Wie wäre es mit Keyword-Argumenten?

>>> g = lambda **kwargs: kwargs 
>>> g(x=1, y=2) 
{'y': 2, 'x': 1} 
>>> g(a='a', b='b') 
{'a': 'a', 'b': 'b'} 

Etwas wie:

g = lambda **kwargs: f(kwargs.get('a', 0), kwargs['b']) 

oder lassen Sie uns sagen, dass Sie nur die Werte verwenden möchten:

>>> g = lambda **kwargs: f(*kwargs.values()) 
>>> def f(*args): print sum(args) 
... 
>>> g(a=1, b=2, c=3) 
6 

In jedem Fall unter Verwendung der **kwargs Syntax Ergebnisse in kwargs ein Wörterbuch des Seins Alle Argumente wurden nach Namen übergeben.

+2

Das Problem mit '** kwargs' ist, dass die Schlüssel des resultierenden Wörterbuchs nicht immer in der Reihenfolge sind, in der Sie sie übergeben haben. – Blender

+0

Das ist ziemlich wahr. –

+0

Die Werte in willkürlicher Reihenfolge bedeuten, dass Ihr zweites Beispiel sie leicht in der falschen Reihenfolge an "f()" übergeben könnte, was die meiste Zeit zählt (obwohl dies nicht mit der gezeigten Funktion geschieht). – martineau

0

können Sie * args verwenden und ** kwargs

Lassen Sie sagen, Sie eine dynamische Funktion zur Laufzeit

def func(): 
    def dyn_func(*args, **kwargs): 
     print args, kwargs 
    return dyn_func 

erzeugen, ist es dann möglich, args in die generierte Funktion zu verwenden

f = func() 
f(test=1) 

würde geben:

() {'test': 1} 

dann ist es möglich, args zu verwalten, wie Sie

1

wollen habe ich ein Gefühl, das man so etwas wie dies will:

import inspect 
import math 

def multiply(x, y): 
    return x * y 

def add(a, b): 
    return a + b 

def cube(x): 
    return x**3 

def pythagorean_theorum(a, b, c): 
    return math.sqrt(a**2 + b**2 + c**2) 

def rpc_command(fname, *args, **kwargs): 
    # Get function by name 
    f = globals().get(fname) 
    # Make sure function exists 
    if not f: 
     raise NotImplementedError("function not found: %s" % fname) 
    # Make a dict of argname: argvalue 
    arg_names = inspect.getargspec(f).args 
    f_kwargs = dict(zip(arg_names, args)) 
    # Add kwargs to the function's kwargs 
    f_kwargs.update(kwargs) 
    return f(**f_kwargs) 

Verbrauch:

>>> # Positional args 
... rpc_command('add', 1, 2) 
3 
>>> 
>>> # Keyword args 
... rpc_command('multiply', x=20, y=6) 
120 
>>> # Keyword args passed as kwargs 
... rpc_command('add', **{"a": 1, "b": 2}) 
3 
>>> 
>>> # Mixed args 
... rpc_command('multiply', 5, y=6) 
30 
>>> 
>>> # Different arg lengths 
... rpc_command('cube', 3) 
27 
>>> 
>>> # Pass in a last as positional args 
... rpc_command('pythagorean_theorum', *[1, 2, 3]) 
3.7416573867739413 
>>> 
>>> # Try a non-existent function 
... rpc_command('doesntexist', 5, 6) 
Traceback (most recent call last): 
    File "<stdin>", line 2, in <module> 
    File "<stdin>", line 6, in rpc_command 
NotImplementedError: function not found: doesntexist 
Verwandte Themen