2017-09-19 6 views
1

Ich verwende ein Python3-Skript, um einige Messdaten zu interpolieren. Dazu möchte ich eine Funktion schreiben, die die Interpolationsfunktion zurückgibt. Diese interpolierende Funktion sollte nur ein bestimmtes benanntes Argument haben. Hier ist eine vereinfachte Version von dem, was ich mit so weit gekommen bin:Zurückgeben einer Funktion, die nur benannte Argumente zulässt

from scipy.interpolate import interp1d 

    x = [1, 2] 
    y = [1, 3] 

    def linear_interp(x, y): 
     # Interpolate the data 
     y_interp = interp1d(x, y) 

     # Create a function taking only a named argument x 
     def y_interp_named(*, x): 
      return y_interp(x) 

     return y_interp_named 

    y_linear_interp = linear_interp(x, y) 

    print(y_linear_interp(x = 1.5)) # This should work 
    print(y_linear_interp(1.5))  # This should throw an error 

Während dies wie vorgesehen funktioniert, ich frage mich, ob es ohne die Verwendung der Funktion y_interp_named in einer eleganteren/kompakt realisiert werden kann.

Allgemein: Gibt es für eine Funktion, die eine andere Funktion zurückgibt, die Möglichkeit, anzugeben, dass die zurückgegebene Funktion nur bestimmte benannte Argumente außer der Definition der zurückgegebenen Funktion verwenden soll?

+1

Nein, erfordert die Unterschrift Ändern einer Schicht aus Indirektion, einfach weil Sie die ursprüngliche Funktion nicht verwenden können. Auch wenn Sie es eleganter machen können, ist dies ohne eine zusätzliche Funktion nicht möglich. – MSeifert

Antwort

2

Sie können Pythons decorators verwenden, um so etwas zu tun.

def named_decorater(func): 
    def func_named(*, x): 
     return func(x) 
    return func_named 

@named_decorater 
def foo(x): 
    print(x) 

foo(2) #error 
foo(x=2) #works 

Edit: Sie können auch den Dekorateur wie folgt verwenden:

named_foo = named_decorator(foo) 
named_foo(2) #error 
named_foo(x=2) #works 
+0

Im Zusammenhang mit der Frage und ihrem Anwendungsfall würde ich denken, dass die wahrscheinlichere Verwendung 'return named_decorator (y_interp)' mit vielleicht einem anderen Funktionsnamen wäre. –

+0

@BrettBeatty Ist die Bearbeitung in Ordnung? – FlashTek

+0

würde ich mir denken –

0

I @ Antwort des FlashTek mögen, aber Lambda könnte eine weitere Option sein.

return lambda *, x: y_interp(x) 

Auch wenn du gehst, das in mehr als einer Stelle zu verwenden, können Sie einen Dekorateur erstellen, die alle Parameter einer Funktion Keyword-only macht. Leider weiß ich nicht genug darüber, und wie es ist, werden alle voreingestellten Parameterwerte in einer dekorierten Funktion gelöscht.

import types 

def kwonly(func): 
    code = types.CodeType(
     0, # new co_argcount 
     func.__code__.co_argcount + func.__code__.co_kwonlyargcount, # new co_kwonlyargcount 
     func.__code__.co_nlocals, 
     func.__code__.co_stacksize, 
     func.__code__.co_flags, 
     func.__code__.co_code, 
     func.__code__.co_consts, 
     func.__code__.co_names, 
     func.__code__.co_varnames, 
     func.__code__.co_filename, 
     func.__code__.co_name, 
     func.__code__.co_firstlineno, 
     func.__code__.co_lnotab, 
     func.__code__.co_freevars, 
     func.__code__.co_cellvars 
    ) 
    return types.FunctionType(code, func.__globals__, func.__name__) 

aber in Ihrem Fall, kann es als

return kwonly(y_interp) 
0

, dass ... Funktion nur bestimmte benannte Argumente

Ich suchte um für "nehmen sollte angegeben werden, erzwinge argspec "," erzwinge Call spec "," obfuscate argspec "und andere Varianten, aber kein Glück.

Etwas Ähnliches kann mit einer Klasse gemacht werden: Sie können Eleganz/Kompaktheit beurteilen.

Das ist Python 2.7 Code, ich denke, 3.6 hat subtile Unterschiede, ich werde für 3.6 aktualisieren, wenn ich kann.

from scipy import interpolate 
class F(object): 
    '''Given X values, iterpolate Y values.''' 
    def __init__(self, x, y): 
     self.interp = interpolate.interp1d(x, y) 
    def __call__(self, **kw): 
     try: 
      arg = kw['x'] 
      return self.interp(arg) 
     except KeyError as e: 
      #something appropriate here 
      raise TypeError('x only', e) 

Nutzungs

>>> f = F([1,2],[1,3]) 
>>> f(x = [1.1,1.11,1.22]) 
array([ 1.2 , 1.22, 1.44]) 

>>> f(y = [1.1,1.11,1.22]) 

Traceback (most recent call last): 
    File "<pyshell#20>", line 1, in <module> 
    f(y = [1.1,1.11,1.22]) 
    File "P:\pyProjects\tmp.py", line 26, in __call__ 
    raise TypeError('x only', e) 
TypeError: ('x only', KeyError('x',)) 
>>> 

>>> f([1.1,1.11,1.22]) 

Traceback (most recent call last): 
    File "<pyshell#19>", line 1, in <module> 
    f([1.1,1.11,1.22]) 
TypeError: __call__() takes exactly 1 argument (2 given) 
>>> 

Eine andere Möglichkeit wäre, ein Klassenattribut akzeptabler Schlüsselwörter definieren dann für die Mitgliedschaft testen:

class F(object): 
    acceptable = {'x','a'} 
    def __init__(self, x, y): 
     self.interp = interpolate.interp1d(x, y) 
    def __call__(self, **kw): 
     if not self.acceptable.issuperset(kw): 
      #something appropriate here 
      difference = set(kw).difference(self.acceptable) 
      raise TypeError('invalid keyword argument', 
          str((list(difference)))) 
     arg = kw['x'] 
     return self.interp(arg) 
Verwandte Themen