2012-04-12 36 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。它已经过很好的测试。