2017-10-10 7 views
3

On m'a demandé d'ajouter une fonctionnalité à la fonction suivante:Comment diviser une fonction avec plusieurs comportements contrôlés par des drapeaux

def maybe_do_thing(x, y, z, really_do_thing): 
    if really_do_thing: 
    return f(x, y, z) # returns a list 
    return []   # an empty list if we did nothing 

Plus précisément, il devrait également être en mesure d'appeler g(x, y, z), contrôlée par un montant supplémentaire drapeau. La mise en œuvre est évidente:

def maybe_do_things(x, y, z, do_f, do_g): 
    results = [] 
    if do_f: 
    results.extend(f(x, y, z)) 
    if do_g: 
    results.extend(g(x, y)) # g needs slightly different params 
    return results 

Pour être clair, un sous-ensemble de f et g peut être appelé: à la fois, non plus, ou un seul, selon les deux drapeaux distincts. f et g sont liés conceptuellement, il est donc logique de les regrouper comme ça, mais cela me semble plutôt insatisfaisant: en réalité, maybe_do_things est juste une multitude de fonctions se déguisant en une seule fonction au moyen de plusieurs drapeaux. Je suis assez sûr que c'est un anti-modèle, mais je ne sais pas comment ça s'appelle ou quel est le bon modèle pour le réparer. Je pense que je devrais être capable de le diviser en plusieurs fonctions autosuffisantes d'une manière qui est toujours aussi pratique à appeler que la fonction composite, ou nettoyer d'une manière ou d'une autre l'API de la fonction composite afin qu'elle ne soit pas un gros blob . Pour chaque site d'appel, il n'est pas très judicieux d'indiquer Il peut être utile que les appelants sachent à l'avance lequel de f et g sera exécuté, mais ces deux paramètres sont déterminés au moment de l'exécution. Ainsi, chaque appelant aurait besoin du corps complet de la fonction en cours, ainsi que des définitions locales représentant les variables x/y/z maintenant répétées.

Je ne pense pas que beaucoup de fonctionnalités seront ajoutées à cette fonction à l'avenir, donc au moins je n'ai pas à m'inquiéter de l'explosion de la liste de paramètres. Peut-être que ma mise en œuvre "évidente" est en fait le meilleur compromis?

+0

Si l'appelant a suffisamment d'informations pour savoir si passer '' do_f' ou do_g', ils ont suffisamment d'informations pour appelez juste le bon eux-mêmes. C'est un peu difficile à analyser sans en savoir plus sur ces drapeaux et où/comment les fonctions sont appelées. Mais une possibilité est de considérer "really_do_thing" comme un "drapeau" indiquant s'il faut appeler "f", d'appeler cet argument "contexte" ou autre. Ensuite, chaque site d'appel passe un "contexte" qui contient toutes les informations nécessaires pour décider quoi faire réellement. Mieux vaut peut-être faire du contexte un objet qui a f, g, etc. comme méthodes. – BrenBarn

+0

Oui, ils ont assez d'informations pour pouvoir appeler eux-mêmes les bonnes fonctions. Mais il est beaucoup plus facile de simplement passer un argument booléen à la fonction 'maybe_do_things', par exemple' maybe_do_things (..., should_i_do_f()) ', que d'écrire l'ensemble de' if should_i_do_f(): f (x, y, z) ', si' f' est une chose compliquée à faire. – amalloy

Répondre

-1

vous pouvez compter combien arg pour chaque fonction.

def maybe_do_thing(funcs, *args): 
    res = [] 
    for func in funcs: 
    num_arg = func.func_code.co_argcount 
    new_arg = args[:num_arg] 
    res.append(func(*new_arg)) 
    return res 

def print_f(string): 
    return string + 'a' 

def print_f2(string, string2): 
    return string + 'a' + string2 

print maybe_do_thing([print_f, print_f2], 'd', 'c') 

['da', 'dac'] 
1

Je ne comprends pas quelle est la raison de la fonction maybe_do_things au lieu d'appeler f(x,y,z) ou g(x,y,z) si l'appelant sait déjà que l'on d'appeler avant de la main, mais une solution pourrait consister à mettre en œuvre le code de prise de décision à l'intérieur maybe_do_things en fonction de 4 résultats possibles: ni, à la fois, f ou g comme ceci:

def maybe_do_things(x,y,z, problem): 
    results = [] 
    decision = decide(problem) 
    if decision == 'both': 
     return results.extend(f(x,y,z)+g(x,y)) 
    elif decision == 'neither': 
     return results 
    elif decision == 'f': 
     return results.extend(f(x,y,z)) 
    elif decidion == 'g': 
     return results.extend(g(x,y))