2012-04-12 34 views
25

我在動態創建python類,並且我知道並非所有字符在此上下文中都有效。Python類名中的有效字符

在類庫中是否有某種方法可用於清理隨機文本字符串,以便我可以將它用作類名?無論是那個或允許的字符列表將是一個很好的幫助。關於與標識符名稱衝突


增加:像@Ignacio在下面的答案中指出,這是valid as an identifier任何字符是一個類名的有效字符。而且你甚至可以毫無困難地使用reserved word作爲類名。但有一個問題。如果確實使用保留字,則無法像其他(非動態創建的)類一樣訪問該類(例如,通過執行globals()[my_class.__name__] = my_class)。在這種情況下,保留字始終優先。

+3

而投票的原因是......?這是一個基本問題,但仍是一個有效的問題:+1。 – EOL 2012-04-12 08:54:17

+0

試圖創建一個名爲'None'或'__debug__'的類是做什麼的?根據以下文檔,我期望它會引發'SyntaxError':https://docs.python.org/2/library/constants.html – ArtOfWarfare 2015-02-23 17:27:36

回答

33

Python Language Reference, §2.3, "Identifiers and keywords"

標識符(也被稱爲名稱)由以下詞法定義描述:

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

標識符的長度是無限的。案例很重要。

+0

完美,謝謝 – 2012-04-12 08:54:17

+2

以下是用於定義有效標識符的正則表達式: 'identifier :: =(letter |「_」)(letter | digit |「_」)*'。 (也許你想添加一些這樣的效果到你的答案,使用戶不必搜索網頁?) – 2014-01-11 19:20:45

+0

迂腐,這不是一個正則表達式@ void-pointer - 這是一個語法。 – Qix 2017-05-28 05:38:35

5

有趣的是,標識符的第一個字符是特殊的。在第一個字符之後,數字'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)) 

這是一種乾淨的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_char的值。但這是你必須處理大多數語言的控制流程的方式! Python的顯式迭代器是一個漂亮的功能,我認爲它使這個代碼更好。

循環顯式迭代器的速度與讓Python爲您隱式獲取迭代器一樣快,顯式迭代器讓我們分離處理標識符不同部分的不同規則的循環。所以顯式迭代器給我們提供了運行速度更快的更乾淨的代碼。贏/贏。

+0

爲什麼你有'itr = iter(seq)'行......不會對'ch中的seq:'具有完全相同的結果,相同的如果不是更好的性能,並提高可讀性? – ArtOfWarfare 2015-02-23 17:30:07

+0

@ArtOfWarfare我編輯了答案解釋。 – steveha 2015-02-24 18:45:43

+0

呵呵。我從來沒有見過這樣做過。下次我需要處理迭代的前後部分時,我會記住這個設計。 – ArtOfWarfare 2015-02-24 19:59:45

1

這是現在一個老問題,但我想添加一個關於如何在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。它已經過很好的測試。