2017-10-21 1 views
1

Mes préoccupations spécifiques sont

  1. Lors de l'analyse d'un paramètre, est intuitif pour un mainteneur futur à comprendre le code qui dépend de lancer une erreur?
  2. Est-il coûteux de lancer des exceptions de façon systématique pour le cas par défaut ? (Semble que cela pourrait être selon https://stackoverflow.com/a/9859202/776940)

Contexte

I ont un paramètre counter qui détermine le nom d'un compteur à incrémenter, et peut éventuellement incrémenter par un entier positif ou négatif séparé du compteur nom par un =. Si aucune valeur d'incrément est fourni, l'amplitude par défaut de l'incrément est 1. La fonction est alimentée par la rupture d'une liste délimitée par des virgules des compteurs et incréments, donc une entrée valable pour l'ensemble du processus peut ressembler à:Est-ce que Pythonic dépend de ValueError lors de l'analyse d'une chaîne de paramètres?

"counter1,counter2=2,counter3=-1" 

qui incrémenter « counter1 » par 1, incrémenter « counter2 » par 2 et décrément « counter3 » par 1.

Comment je l'origine a écrite

counterDescriptor = counterValue.split('=') 
if len(counterDescriptor) == 1: 
    counterName = counterDescriptor[0] 
    counterIncr = 1 
elif len(counterDescriptor) == 2: 
    counterName = counterDescriptor[0] 
    counterIncr = int(counterDescriptor[1]) 
else: 
    counterName, counterIncr = ('counterParsingError', 1) 

qui me frappe, comme je suis récemment revenu à regardez-le, comme trop verbeux et maladroit.

Est-ce un moyen plus ou moins pythonique de coder ce comportement?

def cparse(counter): 
    try: 
     desc,mag = counter.split('=') 
    except ValueError: 
     desc = counter 
     mag = '' 
    finally: 
     if mag == '': 
      mag = 1 
    return desc, int(mag) 

Avec ces cas de test, je vois:

>>> cparse("byfour=4") 
('byfour', 4) 
>>> cparse("minusone=-1") 
('minusone', -1) 
>>> cparse("equalAndNoIncr=") 
('equalAndNoIncr', 1) 
>>> cparse("noEqual") 
('noEqual', 1) 

Ces cas de test qui aurait été pris comment j'ai écrit il (ci-dessus) ne seront pas pris de cette façon:

>>> cparse("twoEquals=2=3") 
('twoEquals=2=3', 1) 
>>> cparse("missingComma=5missingComma=-5") 
('missingComma=5missingComma=-5', 1) 

et ce dernier cas de test ne se bloque pas dans les deux cas. Les deux font la int() vomir:

>>> cparse("YAmissingComma=5NextCounter") 
ValueError: invalid literal for int() with base 10: '5NextCounter' 

Je suis contente d'avoir découvert ce problème en posant cette question. Le service qui consomme cette valeur finira par l'étouffer. Je suppose que je pouvais changer une ligne return desc, int(mag) de la fonction à ceci:

if desc.find("=")<0 and (mag=='0' or (mag if mag.find('..') > -1 else mag.lstrip('-+').rstrip('0').rstrip('.')).isdigit()): 
     return desc, int(mag) 
    else: 
     return 'counterParsingError: {}'.format(desc), 1 

(Pointe du chapeau à https://stackoverflow.com/a/9859202/776940 pour déterminer que c'était le moyen le plus rapide offert dans cette discussion pour déterminer si une chaîne est un entier)

+1

Je envisagerais seulement de gérer ValueErrors ou ce cas si l'analyse d'un objet peut échouer. La façon dont il est utilisé ici ne fait que l'augmenter parce que la liste que vous avez obtenue ne peut pas être décomposée. Je ne le considérerais pas comme pythonique. Même raison pour laquelle nous ne devrions pas gérer AttributeErrors si nous savions que nous avions affaire à des valeurs 'None' alors qu'ils pouvaient simplement tester. –

+0

@JeffMercado elle est augmentée si le paramètre est par défaut 'incrément = 1' et laisse complètement les égales, ou si le paramètre est mal formé (par exemple avec plus d'un égal, ou avec un caractère non-numérique dans l'incrément ou si la chaîne complète de paramètres manquait une virgule entre deux des valeurs du compteur). La façon dont je l'ai fait initialement était explicitement l'analyse et le test, et il me manquait un cas de test unitaire pour 'foo = 3bar = 5' où il devrait y avoir une virgule avant 'bar'. Merci pour les commentaires. –

Répondre

2

Je considère que pythonique, bien que vous pourriez peut-être préférez:

def cparse(counter): 
    if "=" not in counter: 
     # early exit for this expected case 
     return (counter, 1) 
    desc, mag = counter.split("=", maxsplit=1) 
    # note the use of the optional maxsplit to prevent ValueErrors on "a=b=c" 
    # and since we've already tested and short-circuited out of the "no equals" case 
    # we can now consider this handled completely without nesting in a try block. 
    try: 
     mag = int(mag) 
    except ValueError: 
     # can't convert mag to an int, this is unexpected! 
     mag = 1 
    return (desc, mag) 

vous pouvez modifier ceci pour vous assurer d'obtenir la sortie droite lors de l'analyse des chaînes comme a=b=c. Si vous prévoyez recevoir ('a', 1), conservez le code tel quel. Si vous attendez ('a=b', 1), vous pouvez utiliser counter.rsplit au lieu de counter.split.