2016-07-22 29 views
1

Also dachte ich, ich wäre klug und DRY durch Entfernen einer Reihe von gemeinsamen Code aus einer Reihe von ähnlichen Funktionen und verwandelt sie in Hilfsfunktionen alle an einem einzigen Ort definiert. (siehe GitHub diff) So können sie alle von einem einzigen Ort aus geändert werden. (Siehe another GitHub diff)Wie früh aus einer Hilfsfunktion zurückkehren?

So ursprünglich war es

func_A(stuff): 
    if stuff == guard_condition: 
     return early 
    things = boilerplate + stuff 
    do A-specific stuff(things) 
    return late 

func_b(stuff): 
    if stuff == guard_condition: 
     return early 
    things = boilerplate + stuff 
    do B-specific stuff(things) 
    return late 

und ich änderte es

_helper(stuff): 
    if stuff == guard_condition: 
     return early 
    things = boilerplate + stuff 
    return things 

func_A(stuff): 
    things = _helper(stuff) 
    do A-specific stuff(things) 
    return late 

func_B(stuff): 
    things = _helper(stuff) 
    do B-specific stuff(things) 
    return late 

Aber dann habe ich versucht es und erkannte, dass da ich hatte die frühe Rückkehr bewegt („Wächter“ ?) in die Helferfunktion, sie funktionierten natürlich nicht mehr. Jetzt könnte ich den ursprünglichen Funktionen leicht Code hinzufügen, um mit diesen Fällen umzugehen, aber es scheint, dass es keine Möglichkeit gibt, das zu tun, ohne die Komplexität wieder in die einzelnen Funktionen zu verschieben und sich zu wiederholen.

Was ist die eleganteste Art, mit solchen Situationen umzugehen?

+0

ohhh, ist das, was Dekoratoren sind? – endolith

+0

[Verwenden von Python-Dekoratoren, um Wächter zu implementieren] (http://www.siddharta.me/2006/12/using-python-decorators-to-implement.html) – endolith

+0

Ah, aber wenn die zu umbrechenden Funktionen unterschiedliche Anzahl von Argumenten annehmen , müssen Sie '* args' im Decorator verwenden, was dann zur Funktionssignatur wird, die hässlich ist. – endolith

Antwort

2

Sie können a-specific stuff und b-specific stuff zu Kernfunktionen extrahieren, die an Ihre Hilfsfunktion übergeben werden. Dann wird der Helfer entscheiden, ob die Kernfunktionen aufzurufen:

_helper(stuff, _core_func): 
    if stuff == guard_condition: 
     return early 
    things = boilerplate 
    return _core_func(things) 

_a_core(_things): 
    do a-specific stuff 
    return late 

_b_core(_things): 
    do b-specific stuff 
    return late 

func_A(stuff): 
    return _helper(stuff, _a_core) 

func_B(stuff): 
    return _helper(stuff, _b_core) 

FRÜHEREN ANTWORT VOR VERSTÄNDNIS RETURN VALS VON HELFER

würde ich _helper einen Rückgabewert geben:

_helper(stuff): 
    if guard: 
     return False 
    boilerplate 
    return True 

func_a(stuff): 
    if _helper(): 
     do a-specific stuff 
    return 

func_b(stuff): 
    if _helper(): 
     do b-specific stuff 
    return 
+0

hmm, es hat einen Rückgabewert. Ich denke, ich sollte mein Beispiel eher wie den tatsächlichen Code machen – endolith

+0

Sie können das gleiche Prinzip jedoch verwenden. Machen Sie den Rückgabewert zu einem Tupel, bei dem das hinzugefügte Element ist, ob '_helper' den frühen Rückweg oder den vollständigen Pfad durchlaufen hat. – mtrw

+0

Ja, aber das habe ich gemeint, indem ich "die Komplexität wieder zu den ursprünglichen Funktionen hinzufüge" – endolith

2

Hilft das?

def common_stuff(f): 
    def checked_for_guards(*args, **kwargs): 
     if stuff == guard_condition: 
      return early 
     things = boilerplate 
     else: 
      return f(*args, **kwargs) 
    return checked_for_guards 

@common_stuff 
def func_A(stuff): 
    do A-specific stuff(things) 
    return late 

@common_stuff 
def func_b(stuff): 
    do B-specific stuff(things) 
    return late 
+0

Eine Sache, an die ich nicht gedacht habe, ist, dass func_A und func_B eine unterschiedliche Anzahl von Argumenten verwenden, aber das bedeutet, dass die Wrapper-Funktion '* args' verwenden müsste, was dann die Signatur der zu umschließenden Funktion wird hässlich, also denke ich, dass ich die andere Lösung verwenden muss. – endolith

+0

Der obige Code funktioniert für jeden Typ oder Argumente für sowohl func_a und func_b. Die eingepackte Funktion übergibt alle Argumente, die sie empfängt, an func_a oder func_b. – DurgaDatta