私は動的にpython=クラスを作成しています。このコンテキストではすべての文字が有効であるとは限りません。
クラスライブラリのどこかに、ランダムなテキスト文字列をサニタイズしてクラス名として使用できるメソッドがありますか?それか、許可されている文字のリストのいずれかが役立ちます。
識別子名との衝突に関する追加:以下の回答で指摘されている@Ignacioのように、 である任意の文字が識別子として有効 はクラス名で有効な文字です。 予約済みのWord をクラス名として問題なく使用することもできます。しかし、落とし穴があります。予約済みのWordを使用する場合、他の(動的に作成されていない)クラスのようにクラスをアクセス可能にすることはできません(例:globals()[my_class.__name__] = my_class
)。そのような場合、予約語が常に優先されます。
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_]*
これを興味深いものにするのは、識別子の最初の文字が特別であることです。最初の文字の後の数字「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が暗黙的にイテレータを取得するのと同じくらい高速です。明示的なイテレータを使用すると、識別子のさまざまな部分のさまざまなルールを処理するループを分割できます。したがって、明示的なイテレータは、より高速に実行できるよりクリーンなコードを提供します。
これは今では古い質問ですが、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 の下から改作されています。十分にテストされています。