2012-12-12 10 views
5

Ho il seguente codice per creare un contenitore che finge di comportarsi come l'insieme di tutti i numeri primi (in realtà nasconde un test di primo memoised forza bruta)Python argparse scelte da un insieme infinito

import math 

def is_prime(n): 
    if n == 2 or n == 3: 
     return True 
    if n == 1 or n % 2 == 0: 
     return False 
    else: 
     return all(n % i for i in xrange(3, int(1 + math.sqrt(n)), 2)) 


class Primes(object): 

    def __init__(self): 
     self.memo = {} 

    def __contains__(self, n): 
     if n not in self.memo: 
      self.memo[n] = is_prime(n) 
     return self.memo[n] 

Che sembra di lavorare fino ad ora:

>>> primes = Primes() 
>>> 7 in primes 
True 
>>> 104729 in primes 
True 
>>> 100 in primes 
False 
>>> 100 not in primes 
True 

Ma non sta giocando bene con argparse:

>>> import argparse as ap 
>>> parser = ap.ArgumentParser() 
>>> parser.add_argument('prime', type=int, choices=primes, metavar='p') 
_StoreAction(option_strings=[], dest='prime', nargs=None, const=None, default=None, type=<type 'int'>, choices=<__main__.Primes object at 0x7f4e21783f10>, help=None, metavar='p') 
>>> parser.parse_args(['7']) 
Namespace(prime=7) 
>>> parser.parse_args(['11']) 
Namespace(prime=11) 
>>> parser.parse_args(['12']) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python2.7/argparse.py", line 1688, in parse_args 
    args, argv = self.parse_known_args(args, namespace) 
    File "/usr/lib/python2.7/argparse.py", line 1720, in parse_known_args 
    namespace, args = self._parse_known_args(args, namespace) 
    File "/usr/lib/python2.7/argparse.py", line 1929, in _parse_known_args 
    stop_index = consume_positionals(start_index) 
    File "/usr/lib/python2.7/argparse.py", line 1885, in consume_positionals 
    take_action(action, args) 
    File "/usr/lib/python2.7/argparse.py", line 1778, in take_action 
    argument_values = self._get_values(action, argument_strings) 
    File "/usr/lib/python2.7/argparse.py", line 2219, in _get_values 
    self._check_value(action, value) 
    File "/usr/lib/python2.7/argparse.py", line 2267, in _check_value 
    tup = value, ', '.join(map(repr, action.choices)) 
TypeError: argument 2 to map() must support iteration 

Il docs solo dire che

Qualsiasi oggetto che supporta l'operatore in può essere passato come le scelte valore, oggetti in modo dict, oggetti set, contenitori personalizzati, ecc sono tutti supportati.

Ovviamente non voglio ripetere l'infinito "set" di numeri primi. Quindi perché diavolo è argparse provare a map i miei numeri primi? Non ha solo bisogno di in e not in?

risposta

4

Sembra un bug di documentazione. La libreria come scritta richiede che l'argomento choices non sia solo un contenitore ma anche iterabile. Cerca di elencare le opzioni disponibili, che non funzioneranno per il tuo caso. Potresti provare a hackerarlo dandogli un falso __iter__ che restituisce solo una stringa informativa.

Si potrebbe anche voler inviare questo come un bug nel bug tracker Python, dal momento che il comportamento è davvero contraddetto dai documenti.

+0

Grazie, ho creato un problema come suggerito [qui] (http://bugs.python.org/issue16697). – wim

5

La fonte dove si sta ricevendo l'eccezione è abbastanza chiaro, check it out:

if action.choices is not None and value not in action.choices: 
    tup = value, ', '.join(map(repr, action.choices)) 
    msg = _('invalid choice: %r (choose from %s)') % tup 
    raise ArgumentError(action, msg) 

Il controllo di per sé soddisfacente. Si impegna a provare a stampare un messaggio di errore utile, in cui sta cercando di darti tutte le possibili scelte. Suppongo che se si definisce l'iteratore per restituire solo qualcosa che ha come repr la stringa primes, si potrebbe hackerarlo per fare la cosa di destra.

1

choices sono per argomenti che è possibile enumerare tutti i valori consentiti (un insieme limitato (piccolo)). I documenti dovrebbero essere più chiari al riguardo.

primes è un set infinito. È possibile impostare il parametro type per generare ValueError per i non-primi.

Problemi correlati