【问题标题】:Reference class variable in a comprehension of another class variable在另一个类变量的理解中引用类变量
【发布时间】:2012-07-29 18:45:16
【问题描述】:

这可能是一个简单的问题,但我无法对其进行唯一搜索。

我有一个定义静态字典的类,然后尝试定义该字典的子集,也是静态的。

所以,作为一个玩具示例:

class example(object): 
    first_d = {1:1,2:2,3:3,4:4} 
    second_d = dict((k,first_d[k]) for k in (2,3))

这会产生NameError: global name 'first_d' is not defined

我应该如何做这个参考?似乎这种模式在其他情况下也适用,例如:

class example2(object):
    first = 1
    second = first + 1

【问题讨论】:

  • 这不是一个“静态”字典。
  • 为什么不能是实例变量?
  • 嗯,奇怪的是,这很好用:second_d = {k: v for k, v in first_d.items() if k in (2, 3)}
  • @ColinDunklau:我想我想模仿一个枚举。我正在为第 3 方程序编写一个控制器,为此我将组装一个控制参数的文本文件。这些是模块化的,并且在很大程度上是固定的。我的控制器每次应该只运行其中的一个子集。所以我希望每个控制器都有一个实例变量,定义为定义所有可能集合的字典的子集。这里的second_d 是默认集,而不是最终集。如果这不是做事的最佳方式,我很乐意考虑其他方法。
  • @JoelCornett 我将在您的评论中使用解决方案,除非有令人信服的理由不这样做。您能否将其作为答案,以便我接受?

标签: python dictionary class-variables


【解决方案1】:

一个基本的列表理解有以下syntax

[expression for var in iterable]

当类中出现列表推导时,类的属性 可用于iterable。在 Python2 和 Python3 中都是如此。

但是,类的属性可以在 Python2 中的 expression 中使用(即访问),但在 Python3 中则不能。

生成器表达式的故事有点不同:

(expression for var in iterable)

虽然仍然可以从iterable 访问类属性,但不能从expression 访问类属性。 (这适用于 Python2 和 Python3)。

这都可以总结如下:

                             Python2      Python3
Can access class attributes
--------------------------------------------------
list comp. iterable                Y            Y
list comp. expression              Y            N
gen expr. iterable                 Y            Y
gen expr. expression               N            N
dict comp. iterable                Y            Y
dict comp. expression              N            N

(在这方面,字典推导的行为与生成器表达式相同。)


现在这与您的问题有何关系:

在你的例子中,

second_d = dict((k,first_d[k]) for k in (2,3))

出现NameError 是因为无法从生成器表达式的expression 部分访问first_d

Python2 的一种解决方法是将生成器表达式更改为列表推导:

second_d = dict([(k,first_d[k]) for k in (2,3)])

但是,我认为这不是一个非常舒适的解决方案,因为此代码在 Python3 中会失败。

你可以按照 Joel Cornett 的建议去做:

second_d = {k: v for k, v in first_d.items() if k in (2, 3)}

因为这在iterable 中使用first_d 而不是dict 理解的expression 部分。但是,如果first_d 包含许多项目,这可能会遍历更多的项目。不过,如果first_d 很小,这个解决方案可能会很好。

一般来说,你可以通过定义一个可以在类内部或外部定义的辅助函数来避免这个问题:

def partial_dict(dct, keys):
    return {k:dct[k] for k in keys}

class Example(object):
    first_d = {1:1,2:2,3:3,4:4}
    second_d = partial_dict(first_d, (2,3))

class Example2(object):
    a = [1,2,3,4,5]
    b = [2,4]
    def myfunc(A, B):
        return [x for x in A if x not in B]
    c = myfunc(a, b)

print(Example().second_d)
# {2: 2, 3: 3}

print(Example2().c)
# [1, 3, 5]

函数之所以有效,是因为它们定义了一个本地范围,并且 可以从 dict 理解中访问此本地范围内的变量。

这是explained here,但我对此并不完全满意,因为它没有解释为什么expression 部分的行为不同于列表理解、生成器表达式或字典理解的iterable 部分。

因此,我无法(完全)解释为什么Python 会这样,只能说它看起来就是这样。

【讨论】:

  • 感谢 unutbu 的精彩回答。对于尝试使用列表理解/迭代来初始化静态变量(在 python 3 中)的人,这是一个潜在的解决方案:将理解/迭代定义为如上所述的函数(并用 @staticmethod 标记它),例如myfunc(params),然后使用myfunc.__func__(params) 调用它。实际上,我只是编辑答案以包含一个示例。
【解决方案2】:

这有点笨拙,但你可以试试这个:

class test(object):
    pass

test.first = {1:1, 2:2, 3:3, 4:4}
test.second = dict((k, test.first[k]) for k in (2,3))

...然后:

>>> test.first
{1: 1, 2: 2, 3: 3, 4: 4}
>>> test.second
{2: 2, 3: 3}

>>> t = test()
>>> t.first
{1: 1, 2: 2, 3: 3, 4: 4}
>>> t.second
{2: 2, 3: 3}

>>> test.first[5] = 5
>>> t.first
{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}

【讨论】:

  • 任何想法为什么它不起作用,因为它确实有效:second_d = {k: v for k, v in first_d.items() if k in (2, 3)}?
【解决方案3】:

我认为在定义结束之前该类不存在。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    • 2017-02-14
    • 2016-06-11
    • 1970-01-01
    相关资源
    最近更新 更多