7

以下是我的部分类定义:

class Trial:
    font = pygame.font.Font(None, font_size)
    target_dic = {let: font.render(let, True, WHITE, BG) for let in list("ABCDEFGHJKLMNPRSTUVWX")}

部分类定义的最后一行,target_dic = {let: font.render(let, True, WHITE, BG) for let in list("ABCDEFGHJKLMNPRSTUVWX")返回错误:未定义全局名称'font'。很公平。

但是,我尝试了以下测试用例并没有出现错误:

class x:
    dat = 1
    datlist = [dat for i in range(10)]

为什么第一种情况不起作用?font在达到字典理解时该成员不存在吗?

我是否需要将这些操作移到__init__或者是否可以在创建类对象时只定义一次列表?

编辑:

为清楚起见,我希望能够在创建类对象时填充列表,以减少创建 Trial 对象所花费的时间。

4

3 回答 3

4

部分答案,因为它更多的是减少一些错误的路径。

如果我收回您的工作样本并进行 dict 理解:

class x:
    dat = 1
    datlist = {i:dat for i in range(10)}

我也得到这个:

>>> NameError: global name 'dat' is not defined

所以看起来dict理解在类语句执行期间隐藏了临时dict使用,但列表理解没有。

目前在文档中没有找到关于此的更多信息......

根据@interjay 评论进行编辑:在这篇文章中解决了不符合范围规范的类构造。简短的故事是列表理解在 2.x 中存在错误,并且可以看到班级成员,但他们不应该这样做。

于 2012-05-28T17:06:28.670 回答
1

因为类装饰器是在创建类之后调用的,所以您可以使用一个来解决在类主体中引用类属性的限制,并有效地“后处理”刚刚创建的类并添加任何缺少的属性:

def trial_deco(cls):
    cls.target_dic = {let: cls.font.render(let, True, WHITE, BG) for let in "ABCDEFGHJKLMNPRSTUVWXZ"}
    return cls

@trial_deco
class Trial:
    font = pygame.font.Font(None, font_size)
    target_dic = None  # redefined by decorator

另一种方法是使属性最初成为(仅)在第一次读取属性时调用的数据描述符(又名“属性”)。这是通过用计算值覆盖描述符来完成的,所以它只发生一次。

class AutoAttr(object):
    """ data descriptor for just-in-time generation of instance attribute """
    def __init__(self, name, font, antialias, color, background):
        self.data = name, font, antialias, color, background

    def __get__(self, obj, cls=None):
        name, font, antialias, color, background = self.data
        setattr(obj, name, {let: font.render(let, antialias, color, background)
                                for let in "ABCDEFGHJKLMNPRSTUVWXZ"})
        return getattr(obj, name)

class Trial(object):
    font = pygame.font.Font(None, fontsize)    
    target_dic = AutoAttr('target_dic', font, TRUE, WHITE, BG)

还有其他方法可以做这样的事情,例如使用元类或定义__new__方法,但值得怀疑的是,任何一种方法会更好或更简单。

于 2012-05-28T17:45:10.620 回答
-1

在类创建期间可用的名称是全局变量,并且名称已经在类的顶层定义(即,随着名称的定义,它们在类代码的下方变得可用)。

请注意,类级别的对象可能不是您通过实例获得的,甚至不是通过类对象获得的;最值得注意的是,在类创建期间, a 的结果def仍然是一个函数而不是一个方法。

关于 OP 的代码,此示例显示font在类级别定义:

class Trial:
    font = 'foo'
    target_dic = dict((lambda fnt:((let,fnt) for let in "ABCDEFGHJKLMNPRSTUVWX"))(font))
    target_two = []
    for let in "ABCDEFGHJKLMNPRSTUVWX":
        target_two.append(let)

print(Trial.target_dic)
print(Trial.target_two)

这产生:

{'A': 'foo', 'C': 'foo', 'B': 'foo', 'E': 'foo', 'D': 'foo', 'G': 'foo', 'F': 'foo', 'H': 'foo', 'K': 'foo', 'J': 'foo', 'M': 'foo', 'L': 'foo', 'N': 'foo', 'P': 'foo', 'S': 'foo', 'R': 'foo', 'U': 'foo', 'T': 'foo', 'W': 'foo', 'V': 'foo', 'X': 'foo'}
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X']

(http://ideone.com/aC7VY)

font正如预期的那样,可以传递给 lambda。

问题不在于类范围内可用的名称(font在那里可用)。问题是理解引入了自己的新范围(在python 3中):http ://docs.python.org/py3k/reference/expressions.html#displays-for-lists-sets-and-dictionaries

于 2012-05-28T16:42:24.817 回答