2010-02-11 5 views
1

Я попробовал пару подходов, меня действительно интересует только производительность, а не правильность. Я заметил, что реализация на основе регулярного выражения примерно на 3-4 раза медленнее, чем та, которая использует принуждение типа. Есть ли другой, более эффективный способ сделать это?Python: идентификация числовой строки?

def IsNumber(x): 
    try: 
     _ = float(x) 
    except ValueError: 
     return False 
    return True 

def IsNumber2(x): 
    import re 
    if re.match("^\d*.?\d*$", x) == None: 
     return False 
    return True 

Спасибо!

+0

Первый способ является правильным и полезным. Второй косвенный и имеет несколько тонких ошибок. –

+0

@Mike Graham: Ваш комментарий звучит как ответ.Пожалуйста, удалите комментарий, опубликуйте ответ, и мы можем его продвинуть правильно. –

+0

Еще одна ошибка в вашем регулярном выражении: вам нужно избежать этого. перед ? если вы не хотите, чтобы 123z45 считался «числом». –

ответ

6

Прежде всего, они не делают то же самое. Например, поплавки можно указать как «1e3», и float() примет это. Это также не coercion, но конверсия.

Во-вторых, не импортируйте re в IsNumber2, особенно если вы пытаетесь использовать его с timeit. Импортируйте вне функции.

И, наконец, меня не удивляет, что float() работает быстрее. Это специальная процедура, написанная на C для очень конкретной цели, тогда как регулярное выражение должно быть преобразовано в форму, которая интерпретируется.

Является ли ваша первая версия, которая использует float(), достаточно быстро? Это должно быть, и я не знаю, как лучше сделать то же самое в Python.

+0

первая версия достаточно быстрая, я думаю, мне было просто любопытно. Благодаря! – fsm

2

Не совсем. Принуждение - это принятый способ сделать это.

+0

Да, ваш метод выглядит неплохо. Я не удивлен, что метод регулярных выражений медленнее. (во-первых, он импортирует внутреннюю часть функции). –

+0

@Justin: Импортирование 're' во второй раз - это просто вопрос копирования справки из' sys.modules'. Медленная часть компилирует регулярное выражение каждый раз. Операторы импорта –

+0

выполняют некоторую нежелательную блокировку. Это быстрее, чтобы удержать их от функций. – joeforker

0

Сначала вы можете попытаться составить свое регулярное выражение, но я бы предположил, что все равно будет медленнее.

Кроме того, если вы хотите узнать, является ли ваша строка числом, потому что вы собираетесь делать с ней расчеты, вам все равно придется ее принуждать.

+0

Компиляция имеет лишь незначительное влияние, они кэшируются. http://stackoverflow.com/questions/452104/is-it-worth-using-pythons-re-compile – 2010-02-11 22:26:37

2

Ответ зависит от того, что вы подразумеваете под «числовой строкой». Если ваше определение числовой строки - «все, что допускает float», то трудно улучшить метод try-except.

Но имейте в виду, что плавание может быть более либеральным, чем вы хотите: на большинстве машин он примет строки, представляющие бесконечности и nans. Например, на моей машине он принимает 'nan(dead!$#parrot)'. Он также будет принимать начальные и конечные пробелы. И в зависимости от вашего приложения вы можете исключить экспоненциальные представления float. В этих случаях использование регулярного выражения имеет смысл. Чтобы просто исключить бесконечности и nans, было бы проще использовать метод try-except, а затем использовать math.isnan и math.isinf для проверки результата преобразования.

Написание правильного регулярного выражения для числовых строк является удивительно сложной задачей. Например, ваша функция IsNumber2 принимает строку '.'. Вы можете найти проверенную временем версию регулярного выражения с числовой строкой в ​​источнике десятичного модуля. Здесь (с некоторыми незначительными правками):

_parser = re.compile(r"""  # A numeric string consists of: 
    (?P<sign>[-+])?    # an optional sign, followed by either... 
    (
     (?=\d|\.\d)    # ...a number (with at least one digit) 
     (?P<int>\d*)    # having a (possibly empty) integer part 
     (\.(?P<frac>\d*))?  # followed by an optional fractional part 
     (E(?P<exp>[-+]?\d+))? # followed by an optional exponent, or... 
    | 
     Inf(inity)?    # ...an infinity, or... 
    | 
     (?P<signal>s)?   # ...an (optionally signaling) 
     NaN      # NaN 
     (?P<diag>\d*)   # with (possibly empty) diagnostic info. 
    ) 
    \Z 
""", re.VERBOSE | re.IGNORECASE | re.UNICODE).match 

Это в значительной степени матчей именно то, что поплавок принимает, для передних и задних пробелов, за исключением, и некоторые незначительные различия для NaNs (дополнительный «s» для сигнализации пренебрежима малых и диагностическая информация). Когда мне нужно числовое регулярное выражение, я обычно начинаю с этого и редактирую биты, которые мне не нужны.

N.B. Это мыслимое, что float может быть медленнее, чем регулярное выражение, так как он должен не только разбирать строку, но также превращать ее в float, что является довольно сложным вычислением; однако это все равно было бы сюрпризом.