【问题标题】:Dictionary class attribute that refers to other class attributes in the definition引用定义中其他类属性的字典类属性
【发布时间】:2015-11-22 07:58:21
【问题描述】:

虽然有很多方法可以解决这个问题,但由于性格缺陷,在我了解失败的性质之前,我不能放手。

尝试:

class OurFavAnimals(object):
    FAVE = 'THATS ONE OF OUR FAVORITES'
    NOTFAVE = 'NAH WE DONT CARE FOR THAT ONE'
    UNKNOWN = 'WHAT?'
    FAVES = defaultdict(lambda: UNKNOWN, {x:FAVE for x in ['dog', 'cat']})
    FAVES['Crab'] = NOTFAVE 

失败:

      3     NOTFAVE = 'NAH WE DONT CARE FOR THAT ONE'
      4     UNKNOWN = 'WHAT?'
----> 5     FAVES = defaultdict(lambda: UNKNOWN, {x:FAVE for x in ['dog', 'cat']})
      6     FAVES['Crab'] = NOTFAVE

NameError: global name 'FAVE' is not defined

为什么?为什么它可以找到UNKNOWN 而不是FAVE?是因为它在字典理解中吗?

【问题讨论】:

    标签: python dictionary class-attributes


    【解决方案1】:

    是的,这是因为它在字典理解中。请注意,它也不是“找到”UNKNOWN;它只是还没有寻找它,因为 UNKNOWN 仅在 lambda 中引用。如果您将 dict 理解替换为其他内容以允许类定义成功,则稍后如果您尝试访问不存在的键(因为它将尝试调用该 lambda),您将收到错误消息。所以如果你把它改成

    FAVES = defaultdict(lambda: UNKNOWN, {'a': 1})
    

    你会得到:

    >>> OurFavAnimals.FAVES['x']
    Traceback (most recent call last):
      File "<pyshell#4>", line 1, in <module>
        OurFavAnimals.FAVES['x']
      File "<pyshell#2>", line 5, in <lambda>
        FAVES = defaultdict(lambda: UNKNOWN, {'a': 1})
    NameError: global name 'UNKNOWN' is not defined
    

    在这两种情况下,原因是在类作用域中定义的变量在嵌套作用域中不可用。换句话说,这与失败的原因相同:

    class Foo(object):
        something = "Hello"
        def meth(self):
            print(something)
    

    lambda 和字典推导式都创建嵌套在类作用域中的函数作用域,因此它们无法直接访问类变量。另见this related question

    【讨论】:

    • 我要补充一点,解决这个问题的最可取的方法是在类的 __init__() 方法中使用 self.FAVE = [...] 定义(部分或全部)这些变量。
    • 谢谢!但这仍然很奇怪。为什么会这样?我认为嵌套作用域总是可以访问 Python 中的父作用域。至少,根据我的经验,以前一直都是这样。
    • @eriophora:类范围不同。请参阅我链接到的问题以进行一些讨论。
    • W.R.T. Sam 的回答,虽然这里不是这种情况,而且很难想象会出现这种情况的场景,但假设您必须使用相同的参数初始化该类的数十万个实例。我有一个精心设计的背景故事来证明它的合理性(“假设你想模拟一大群可以相互即时交流的通灵者,他们环游世界,学习新动物的名字,并任意将它们分配给他们共同的‘最爱’或'不喜欢'的动物列表...")
    • @BrenBarn 你链接的答案太棒了;我很惭愧我没有早点找到它。也许人们应该在堆栈溢出上获得分数,以便用问题的替代措辞来标记已解决的问题。或者也许我应该在我的搜索词中包含“列表理解”......
    猜你喜欢
    • 1970-01-01
    • 2019-09-02
    • 2011-09-09
    • 1970-01-01
    • 2013-01-26
    • 1970-01-01
    • 2019-06-09
    • 2012-02-09
    • 2016-03-03
    相关资源
    最近更新 更多