web-dev-qa-db-ja.com

pythonクラス名の有効な文字

私は動的にpython=クラスを作成しています。このコンテキストではすべての文字が有効であるとは限りません。

クラスライブラリのどこかに、ランダムなテキスト文字列をサニタイズしてクラス名として使用できるメソッドがありますか?それか、許可されている文字のリストのいずれかが役立ちます。


識別子名との衝突に関する追加:以下の回答で指摘されている@Ignacioのように、 である任意の文字が識別子として有効 はクラス名で有効な文字です。 予約済みのWord をクラス名として問題なく使用することもできます。しかし、落とし穴があります。予約済みのWordを使用する場合、他の(動的に作成されていない)クラスのようにクラスをアクセス可能にすることはできません(例:globals()[my_class.__name__] = my_class)。そのような場合、予約語が常に優先されます。

28
Filipe Correia

Python言語リファレンス、§2.3、「識別子とキーワード」

識別子(namesとも呼ばれます)は、次の字句定義によって記述されます。

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

識別子の長さは無制限です。ケースは重要です。

Python言語リファレンス、§2.3、「識別子とキーワード」 のとおり、有効なPython識別子は次のように定義されます。

(letter|"_") (letter | digit | "_")*

または、regexの場合:

[a-zA-Z_][a-zA-Z0-9_]*
6
sevko

これを興味深いものにするのは、識別子の最初の文字が特別であることです。最初の文字の後の数字「0」から「9」は識別子として有効ですが、最初の文字であってはなりません。

これは、ランダムな文字列が与えられると有効な識別子を返す関数です。仕組みは次のとおりです。

まず、itr = iter(seq)を使用して、入力の明示的なイテレータを取得します。次に、最初のループがあり、イテレータitrを使用して、識別子の有効な最初の文字が見つかるまで文字を調べます。次に、そのループから抜け出し、2番目のループを実行します同じイテレータを使用して(これはitrという名前です)。イテレータitrは私たちのために私たちの場所を保ちます。最初のループがイテレーターから引き出された文字は、2番目のループが実行されても失われます。

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))

これは、シーケンスを2つの異なる方法で処理するためのクリーンでPython的な方法です。この単純な問題の場合、最初の文字をまだ見たかどうかを示すブール変数を用意するだけです。

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

このバージョンは最初のバージョンほど好きではありません。 1つの文字の特別な処理は、制御のフロー全体で複雑になり、saw_first_charの値を常にチェックし続ける必要があるため、最初のバージョンよりも遅くなります。しかし、これはほとんどの言語で制御のフローを処理しなければならない方法です! Pythonの明示的なイテレータは気の利いた機能であり、このコードを大幅に改善すると思います。

明示的なイテレータでのループは、Pythonが暗黙的にイテレータを取得するのと同じくらい高速です。明示的なイテレータを使用すると、識別子のさまざまな部分のさまざまなルールを処理するループを分割できます。したがって、明示的なイテレータは、より高速に実行できるよりクリーンなコードを提供します。

5
steveha

これは今では古い質問ですが、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 の下から改作されています。十分にテストされています。

1
Ghostkeeper