Как получить функциональность Python isidentifer () в Python 2.6? - PullRequest
16 голосов
/ 30 марта 2010

В Python 3 есть строковый метод с именем str.isidentifier

Как я могу получить аналогичную функциональность в Python 2.6, если не считать переписывания моего собственного регулярного выражения и т. Д .?

Ответы [ 8 ]

14 голосов
/ 30 марта 2010

модуль tokenize определяет регулярное выражение с именем Name

import re, tokenize, keyword
re.match(tokenize.Name + '$', somestr) and not keyword.iskeyword(somestr)
2 голосов
/ 23 апреля 2019

Проверка неверного идентификатора


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

Шаблоны регулярных выражений, предложенные в других ответах, построены из tokenize.Name, который содержит следующий шаблон регулярных выражений [a-zA-Z_]\w* (работает на python 2.7.15) и '$' привязку регулярных выражений.

Пожалуйста, обратитесь к официальному описанию Python 3 идентификаторов и ключевых слов (которое содержит абзац, который также относится к Python 2).

В пределах диапазона ASCII (U + 0001..U + 007F) допустимые символы для идентификаторов такие же, как в Python 2.x: прописные и строчные буквы от A до Z, подчеркивание _ и, за исключением первый символ, цифры от 0 до 9.

таким образом, 'foo \ n' не должен считаться действительным идентификатором.

Хотя можно утверждать, что этот код функционален:

>>>  class Foo():
>>>     pass
>>> f = Foo()
>>> setattr(f, 'foo\n', 'bar')
>>> dir(f)
['__doc__', '__module__', 'foo\n']
>>> print getattr(f, 'foo\n')
bar

Поскольку символ перевода строки действительно является допустимым символом ASCII, он не считается буквой. Более того, явно нет практического использования идентификатора, который заканчивается символом новой строки

>>> f.foo\n
SyntaxError: unexpected character after line continuation character

Функция str.isidentifier также подтверждает, что это неверный идентификатор:

Python3 переводчик:

>>> print('foo\n'.isidentifier())
False

Якорь $ против якоря \Z


Цитирование официального синтаксиса регулярного выражения python2 :

$

Соответствует концу строки или непосредственно перед новой строкой в ​​конце строки, а в режиме MULTILINE также соответствует перед новой строкой. foo совпадает как с «foo», так и с «foobar», а регулярное выражение foo $ соответствует только «foo». Что еще интереснее, поиск foo. $ В 'foo1 \ nfoo2 \ n' обычно соответствует 'foo2', но 'foo1' в режиме MULTILINE; при поиске одного $ в 'foo \ n' найдутся два (пустых) совпадения: одно непосредственно перед новой строкой, а другое в конце строки.

В результате получается строка, заканчивающаяся символом новой строки, который соответствует действительному идентификатору:

>>> import tokenize
>>> import re
>>> re.match(tokenize.Name + '$', 'foo\n')
<_sre.SRE_Match at 0x3eac8e0>
>>> print m.group()
'foo'

Шаблон регулярного выражения не должен использовать якорь $, но вместо этого \Z - это якорь, который следует использовать. Цитирую еще раз:

\Z

Соответствует только в конце строки.

А теперь правильное регулярное выражение:

>>> re.match(tokenize.Name + r'\Z', 'foo\n') is None
True

Опасные последствия


См. Ответ Люка , где приведен еще один пример того, как такого рода слабое сопоставление регулярных выражений может потенциально в других обстоятельствах иметь более опасные последствия.

Дальнейшее чтение


В Python 3 добавлена ​​поддержка не-ascii идентификаторов, см. PEP-3131 .

2 голосов
/ 30 марта 2010
re.match(r'[a-z_]\w*$', s, re.I)

должно быть хорошо. Насколько я знаю, встроенного метода не существует.

1 голос
/ 30 марта 2010

Я решил сделать еще одну попытку, поскольку было несколько хороших предложений. Я постараюсь объединить их. Следующее может быть сохранено как модуль Python и запущено непосредственно из командной строки. При запуске он проверяет эту функцию, поэтому он корректно корректен (по крайней мере, в той мере, в которой документация демонстрирует эту возможность).

import keyword
import re
import tokenize

def isidentifier(candidate):
    """
    Is the candidate string an identifier in Python 2.x
    Return true if candidate is an identifier.
    Return false if candidate is a string, but not an identifier.
    Raises TypeError when candidate is not a string.

    >>> isidentifier('foo')
    True

    >>> isidentifier('print')
    False

    >>> isidentifier('Print')
    True

    >>> isidentifier(u'Unicode_type_ok')
    True

    # unicode symbols are not allowed, though.
    >>> isidentifier(u'Unicode_content_\u00a9')
    False

    >>> isidentifier('not')
    False

    >>> isidentifier('re')
    True

    >>> isidentifier(object)
    Traceback (most recent call last):
    ...
    TypeError: expected string or buffer
    """
    # test if candidate is a keyword
    is_not_keyword = candidate not in keyword.kwlist
    # create a pattern based on tokenize.Name
    pattern_text = '^{tokenize.Name}$'.format(**globals())
    # compile the pattern
    pattern = re.compile(pattern_text)
    # test whether the pattern matches
    matches_pattern = bool(pattern.match(candidate))
    # return true only if the candidate is not a keyword and the pattern matches
    return is_not_keyword and matches_pattern

def test():
    import unittest
    import doctest
    suite = unittest.TestSuite()
    suite.addTest(doctest.DocTestSuite())
    runner = unittest.TextTestRunner()
    runner.run(suite)

if __name__ == '__main__':
    test()
1 голос
/ 30 марта 2010

Хорошие ответы до сих пор. Я бы написал это так.

import keyword
import re

def isidentifier(candidate):
    "Is the candidate string an identifier in Python 2.x"
    is_not_keyword = candidate not in keyword.kwlist
    pattern = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
    matches_pattern = bool(pattern.match(candidate))
    return is_not_keyword and matches_pattern
1 голос
/ 30 марта 2010

В Python <3.0 это довольно просто, так как вы не можете иметь символы юникода в идентификаторах. Это должно сделать работу: </p>

import re
import keyword

def isidentifier(s):
    if s in keyword.kwlist:
        return False
    return re.match(r'^[a-z_][a-z0-9_]*$', s, re.I) is not None
0 голосов
/ 06 марта 2016

Все предлагаемые решения не поддерживают Unicode и не допускают число в первом символе , если работают на Python 3 .

Редактировать: предложенные решения должны использоваться только на Python 2, а на Python3 isidentifier. Вот решение, которое должно работать где угодно:

re.match(r'^\w+$', name, re.UNICODE) and not name[0].isdigit()

По сути, он проверяет, состоит ли что-то из (как минимум 1) символов (включая цифры), а затем проверяет, не является ли первый символ числом.

0 голосов
/ 30 марта 2010

Что я использую:

def is_valid_keyword_arg(k):
    """
    Return True if the string k can be used as the name of a valid
    Python keyword argument, otherwise return False.
    """
    # Don't allow python reserved words as arg names
    if k in keyword.kwlist:
        return False
    return re.match('^' + tokenize.Name + '$', k) is not None
...