33

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

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


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

4

4 に答える 4

36

Python 3

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

Pythonでの識別子の構文は、Unicode標準の付録UAX-31に基づいており、以下に定義されているように詳細と変更が加えられています。詳細については、PEP3131も参照してください。

ASCII範囲(U + 0001..U + 007F)内では、識別子の有効な文字はPython 2.xと同じです。大文字と小文字のAからZ、アンダースコア_、および最初の文字を除いて、 0から9までの数字。

Python 3.0では、ASCII範囲外の追加の文字が導入されています(PEP 3131を参照)。これらの文字の分類では、unicodedataモジュールに含まれているUnicode文字データベースのバージョンが使用されます。

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

identifier   ::=  xid_start xid_continue*
id_start     ::=  <all characters in general categories Lu, Ll, Lt, Lm, Lo, Nl, the underscore, and characters with the Other_ID_Start property>
id_continue  ::=  <all characters in id_start, plus characters in the categories Mn, Mc, Nd, Pc and others with the Other_ID_Continue property>
xid_start    ::=  <all characters in id_start whose NFKC normalization is in "id_start xid_continue*">
xid_continue ::=  <all characters in id_continue whose NFKC normalization is in "id_continue*">

上記のUnicodeカテゴリコードは次の略です。

  • Lu-大文字
  • Ll-小文字
  • Lt-タイトルケース文字
  • Lm-修飾子文字
  • Lo-他の文字
  • Nl-文字番号
  • Mn-間隔のないマーク
  • Mc-スペーシング結合マーク
  • Nd-10進数
  • PC-コネクタの句読点
  • Other_ID_Start-下位互換性をサポートするためのPropList.txt内の文字の明示的なリスト
  • Other_ID_Continue-同様に

すべての識別子は、解析中に通常の形式のNFKCに変換されます。識別子の比較はNFKCに基づいています。

Unicode 4.1のすべての有効な識別子文字をリストした非規範的なHTMLファイルは、https://www.dcl.hpi.uni-potsdam.de/home/loewis/table-3131.htmlにあります。

Python 2

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

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

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

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

于 2012-04-12T08:52:14.927 に答える
9

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

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

または、正規表現で:

[a-zA-Z_][a-zA-Z0-9_]*
于 2014-07-02T21:37:05.193 に答える
7

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

以下は、任意のランダムな文字列を指定して有効な識別子を返す関数です。仕組みは次のとおりです。

まず、itr = iter(seq)入力で明示的な反復子を取得するために使用します。次に、最初のループがあり、イテレータを使用してitr、識別子の有効な最初の文字が見つかるまで文字を調べます。次に、そのループから抜け出し、2 番目のループに同じ反復子( という名前を付けましitrた) を使用して 2 番目のループを実行します。イテレータ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 つの異なる方法で処理するためのクリーンで Pythonic な方法です。この単純な問題の場合、最初の文字をまだ見ていないかどうかを示すブール変数を使用できます。

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_char1 つの文字の特別な処理が制御の流れ全体に絡み合っており、常にの値をチェックし続ける必要があるため、最初のバージョンよりも遅くなります。しかし、これはほとんどの言語で制御の流れを処理する必要がある方法です! Python の明示的なイテレータは気の利いた機能で、このコードを大幅に改善していると思います。

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

于 2012-04-12T09:39:38.643 に答える
3

これは今では古い質問ですが、実装を行ったので、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から適用されます。それは十分にテストされています。

于 2017-01-10T03:34:32.573 に答える