Ich schreibe eine Klasse, die eine Reihe von Methoden arbeitet auf ähnliche Arten von Argumenten hat:Pythonic Art und Weise der Parameter auf den gleichen Standard in allen Methoden einer Klasse Umwandlung
class TheClass():
def func1(self, data, params, interval):
....
def func2(self, data, params):
....
def func3(self, data, interval):
....
def func4(self, params):
....
...
Es gibt eine bestimmte Konvention über diese Argumente (zB data
/params
sollte numpy.farrays
, interval
- ein list
von 2 floats
sein), aber ich möchte dem Benutzer erlauben, mehr Freiheit zu haben: zB Funktionen sollten int
als data
oder params
oder nur eine int
als interval
annehmen und dann davon ausgehen, dass dies der Endpunkt mit dem Startpunkt ist 0
usw.
So all diese Transformationen innerhalb von Methoden zu vermeiden, sollte das nur tun die Logik, ich bin mit Dekorateure wie folgt aus:
def convertparameters(*types):
def wrapper(func):
def new_func(self, *args, **kwargs):
# Check if we got enough parameters
if len(types) > len(args):
raise Exception('Not enough parameters')
# Convert parameters
new_args = list(args)
for ind, tip in enumerate(types):
if tip == "data":
new_args[ind] = _convert_data(new_args[ind])
elif tip == "params":
new_args[ind] = _convert_params(new_args[ind])
elif tip == "interval":
new_args[ind] = _convert_interval(new_args[ind])
else:
raise Exception('Unknown type for parameter')
return func(self, *new_args, **kwargs)
return new_func
return wrapper
Wo _convert_data
, _convert_params
und _convert_interval
die schmutzige Arbeit zu tun. Dann definiere ich die Klasse wie folgt:
class TheClass():
@convertparameters("data", "params", "interval")
def func1(self, data, params, interval):
....
@convertparameters("data", "params")
def func2(self, data, params):
....
@convertparameters("data", "interval")
def func3(self, data, interval):
....
@convertparameters("params")
def func4(self, params):
....
...
Es funktioniert der Trick, aber es sind einige sehr störend Dinge:
- Es clutters der Code (obwohl es ein kleines Problem ist, und ich finde, Diese Lösung ist viel kompakter als der explizite Aufruf der Transformationsfunktion zu Beginn jeder Methode.
- Wenn ich diesen Dekorator mit einem anderen Decorator kombinieren muss (
@staticmethod
oder etwas, das die Ausgabe der Methode nachbearbeitet), dann muss die Reihenfolge geändert werden diese Dekorateure sind wichtig - Die beunruhigendste Sache ist, dass der Dekorateur dieses Formular vollständig aus der Parameterstruktur des Verfahrens versteckt und IPython zeigt es als
func1(*args, **kwargs)
Gibt es eine bessere (oder mehr „Pythonic“) Wege Machen Sie solche massive Parametertransformation?
UPDATE 1: SOLUTION basierend auf n9code ‚s Vorschlag
Um Verwirrung zu vermeiden, eine Modifikation des convertparameters
Wrapper gibt es die dritte Ausgabe löst (Maskierung der Unterzeichnung und docstring von Methoden) - durch vorgeschlagen n9code für Python> 2.5.
Mit decorator
Modul (separat installiert: pip install decorator
) wir alle „Metadaten“ die Funktion übertragen können (docstring, Name und Unterschrift) zur gleichen Zeit von verschachtelten Struktur im Inneren der Hülle loszuwerden
from decorator import decorator
def convertparameters(*types):
@decorator
def wrapper(func, self, *args, **kwargs):
# Check if we got enough parameters
if len(types) > len(args):
raise Exception('Not enough parameters')
# Convert parameters
new_args = list(args)
for ind, tip in enumerate(types):
if tip == "data":
new_args[ind] = _convert_data(new_args[ind])
elif tip == "params":
new_args[ind] = _convert_params(new_args[ind])
elif tip == "interval":
new_args[ind] = _convert_interval(new_args[ind])
else:
raise Exception('Unknown type for parameter')
return func(self, *new_args, **kwargs)
return wrapper
UPDATE 2: MODIFIED SOLUTION basierend auf zmbq ‚s Vorschlag
Mit inspect
Modul wir auch Argumente von Dekorateur loswerden könnte, überprüft die Namen von argu der ursprünglichen Funktion.Dies wird eine andere Schicht des Dekorators
beseitigen und die Verwendung ist dann viel einfacher. Die einzige wichtige Sache ist, die gleichen Namen für Argumente zwischen verschiedenen Funktionen zu verwenden.
class TheClass():
@convertparameters
def func1(self, data, params, interval):
....
@convertparameters
def func2(self, data, params):
....
Danke, ich wusste nicht über 'Wraps'. Ich nehme an, es sollte auch 'return new_func' und' return wrapper' geben? – Vladimir
Ich habe noch eine Frage: 'Wraps' überträgt den Docstring korrekt. Die Signatur bleibt jedoch 'func1 (* args, ** kwargs)'. Gibt es eine Möglichkeit, auch die Signatur zu übertragen? – Vladimir
Ich vermute, Sie verwenden Python 2. Im Falle von Python 3 würde die Signatur auch erhalten bleiben. Schau dir mein Update an. – bagrat