Подтвердить что ты не робот

Действительные символы в имени класса python

Я динамически создаю классы python, и я знаю, что не все символы действительны в этом контексте.

Есть ли метод где-то в библиотеке классов, который я могу использовать для дезинфекции случайной текстовой строки, чтобы я мог использовать ее как имя класса? Либо это, либо список допустимых символов будет хорошей помощью.


Дополнение о конфликтах с именами идентификаторов:. Как @Ignacio указал в ответе ниже, любой символ, который действителен как идентификатор является допустимым символом в имени класса. И вы даже можете без проблем использовать зарезервированное слово как имя класса. Но есть улов. Если вы используете зарезервированное слово, вы не сможете сделать класс доступным, как другие (нединамически созданные) классы (например, выполнив globals()[my_class.__name__] = my_class). В этом случае зарезервированное слово всегда будет иметь приоритет.

4b9b3361

Ответ 1

Справочник по языку Python, §2.3, "Идентификаторы и ключевые слова"

Идентификаторы (также называемые именами) описываются следующими лексическими определениями:

identifier ::=  (letter|"_") (letter | digit | "_")*
letter     ::=  lowercase | uppercase
lowercase  ::=  "a"..."z"
uppercase  ::=  "A"..."Z"
digit      ::=  "0"..."9"

Идентификаторы неограниченны по длине. Дело значимо.

Ответ 3

Вещь, которая делает это интересным, заключается в том, что первый символ идентификатора является особым. После первого символа цифры от 0 до 9 соответствуют действительным для идентификаторов, но они не должны быть первым символом.

Здесь функция, которая вернет действительный идентификатор, заданный любой случайной строкой символов. Вот как это работает:

Сначала мы используем itr = iter(seq), чтобы получить явный итератор на входе. Тогда есть первый цикл, который использует итератор itr для просмотра символов до тех пор, пока не найдет допустимый первый символ для идентификатора. Затем он вырывается из этого цикла и запускает второй цикл, используя тот же самый итератор (который мы назвали itr) для второго цикла. Итератор itr хранит наше место для нас; символы, которые первый цикл вытащил из итератора, все еще исчезают, когда выполняется второй цикл.

def gen_valid_identifier(seq):
    # get an iterator
    itr = iter(seq)
    # pull characters until we get a legal one for first in identifer
    for ch in itr:
        if ch == '_' or ch.isalpha():
            yield ch
            break
    # pull remaining characters and yield legal ones for identifier
    for ch in itr:
        if ch == '_' or ch.isalpha() or ch.isdigit():
            yield ch

def sanitize_identifier(name):
    return ''.join(gen_valid_identifier(name))

Это чистый и путинский способ обработки последовательности двумя разными способами. Для проблемы это просто, мы могли бы просто иметь логическую переменную, которая указывает, видели ли мы еще один символ или нет:

def gen_valid_identifier(seq):
    saw_first_char = False
    for ch in seq:
        if not saw_first_char and (ch == '_' or ch.isalpha()):
            saw_first_char = True 
            yield ch
        elif saw_first_char and (ch == '_' or ch.isalpha() or ch.isdigit()):
            yield ch

Мне не нравится эта версия почти так же, как и первая версия. Специальная обработка для одного символа теперь запутана во всем потоке управления, и это будет медленнее, чем первая версия, так как она должна постоянно проверять значение saw_first_char. Но именно так вы должны были бы управлять потоком управления на большинстве языков! Явный итератор Python - отличная функция, и я думаю, что этот код намного лучше.

Looping на явном итераторе выполняется так же быстро, как позволить Python неявно получить для вас итератор, а явный итератор позволяет нам разделить циклы, обрабатывающие разные правила для разных частей идентификатора. Таким образом, явный итератор дает нам более чистый код, который также работает быстрее. Win/выигрыш.

Ответ 4

Это старый вопрос, но я хотел бы добавить ответ о том, как это сделать в Python 3, поскольку я сделал реализацию.

Разрешенные символы описаны здесь: https://docs.python.org/3/reference/lexical_analysis.html#identifiers. Они включают в себя довольно много специальных персонажей, включая знаки препинания, подчеркивания и целое количество иностранных персонажей. К счастью, модуль unicodedata может помочь. Здесь моя реализация напрямую реализует то, что говорит документация Python:

import unicodedata

def is_valid_name(name):
    if not _is_id_start(name[0]):
        return False
    for character in name[1:]:
        if not _is_id_continue(character):
            return False
    return True #All characters are allowed.

_allowed_id_continue_categories = {"Ll", "Lm", "Lo", "Lt", "Lu", "Mc", "Mn", "Nd", "Nl", "Pc"}
_allowed_id_continue_characters = {"_", "\u00B7", "\u0387", "\u1369", "\u136A", "\u136B", "\u136C", "\u136D", "\u136E", "\u136F", "\u1370", "\u1371", "\u19DA", "\u2118", "\u212E", "\u309B", "\u309C"}
_allowed_id_start_categories = {"Ll", "Lm", "Lo", "Lt", "Lu", "Nl"}
_allowed_id_start_characters = {"_", "\u2118", "\u212E", "\u309B", "\u309C"}

def _is_id_start(character):
    return unicodedata.category(character) in _allowed_id_start_categories or character in _allowed_id_start_categories or unicodedata.category(unicodedata.normalize("NFKC", character)) in _allowed_id_start_categories or unicodedata.normalize("NFKC", character) in _allowed_id_start_characters

def _is_id_continue(character):
    return unicodedata.category(character) in _allowed_id_continue_categories or character in _allowed_id_continue_characters or unicodedata.category(unicodedata.normalize("NFKC", character)) in _allowed_id_continue_categories or unicodedata.normalize("NFKC", character) in _allowed_id_continue_characters

Этот код адаптирован здесь под CC0: https://github.com/Ghostkeeper/Luna/blob/d69624cd0dd5648aec2139054fae4d45b634da7e/plugins/data/enumerated/enumerated_type.py#L91. Он был хорошо протестирован.