在类定义中执行语句:解释器知道哪些变量?

时间:2012-05-28 16:27:03

标签: python

以下是我的部分类定义:

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__,或者在创建类对象时是否可以准确定义一次列表?

编辑:

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

3 个答案:

答案 0 :(得分:4)

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

如果我收回你的工作样本并提出理解:

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

我也得到了这个:

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

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

目前在关于此的文档中找不到进一步的信息...

根据@interjay评论编辑: 在post中解决了不符合范围规范的阶级结构。简短的故事是,列表理解在2.x中是错误的并且看到了班级成员,但他们不应该。

答案 1 :(得分: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__方法,但是任何方法都可以更好地工作或者更复杂。

答案 2 :(得分:-1)

在创建类时可用的名称是全局变量,并且在类的顶层定义了名称​​已经(也就是说,当名称被定义时,它们在课程的下方可用码)。

请注意,类级别的对象可能不是通过实例获取它们,甚至不是通过类对象获取它们;最值得注意的是,在课堂创作过程中,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