【问题标题】:Python 3 - Class variable is not defined [duplicate]Python 3 - 未定义类变量[重复]
【发布时间】:2018-04-23 17:46:44
【问题描述】:

在这里,我无法访问 Python 中的类变量 列表理解

class Student:
  max_year = 18
  year_choice = [i for i in range(100) if i > max_year]

  def __init__(self, name):
    self.name = name
    print (self.year_choice)

Student('Blah')

但它在 Python 2 中运行良好。

../workspace$ python student.py
[19, 20, 21, 22, 2.... 99]

但在 Python 3 中出现错误。

../workspace$ python student.py
File "student.py", line 18, in <listcomp>
year_choice = [i for i in range(100) if i > max_year]
NameError: name 'max_year' is not defined

从调试这个当我改变 下面的语句

[i for i in range(100) if i &gt; max_year]

到这里

[i for i in range(max_year)] # don't look too much into it ;)

工作正常。 为什么我无法访问 if/else 列表理解中的类变量?

【问题讨论】:

  • 好吧,这很奇怪。 ;) 此外,它与列表组合中的if 无关。这也不起作用:[max_year-i for i in range(max_year)]
  • 是来自@py_dude 答案,我们可以定义 self.max_year 它与该类的类和实例共享。不仅适用于这个例子。但是在这里我很想知道为什么我们不能访问 if/else 列表理解中的类变量?
  • 好问题,但它仍然是重复的;-)
  • 对于range(max_year),它之所以有效,是因为range(max_year)为列表理解创建的范围之外进行评估,但if 子句被评估新范围内。
  • 我在回答中添加了一些代码来说明 Antti Haapala 的解释。

标签: python python-3.x scope


【解决方案1】:

这条线的原因

year_choice = [i for i in range(100) if i > max_year]

适用于 Python 2 但不适用于 Python 3 的原因在于,在 Python 3 中,列表推导式创建了一个新范围,而 max_year 类属性不在该范围内。在 Python 2 中,列表推导不会创建新的作用域,它在周围代码的上下文中运行。最初是出于性能原因这样做,但很多人觉得它令人困惑,因此在 Python 3 中对其进行了更改,使列表推导式与生成器表达式以及集合和字典推导式保持一致。

AFAIK,在 Python 3 中没有简单的方法可以访问在类的外部上下文中运行的列表推导内的类属性,而不是在方法内。您不能使用 Student.max_year 引用它,因为此时 Student 类不存在。

但是,无论如何,在那里进行列表理解确实没有意义。您可以更紧凑、更高效地创建您想要的列表。例如:

class Student(object):
    max_year = 18
    year_choice = list(range(max_year + 1, 100))

    def __init__(self, name):
        self.name = name
        print (self.year_choice)

Student('Blah')

输出

[19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

该代码在 Python 2 和 Python 3 上产生相同的输出。

我已将类签名更改为

class Student(object):

以便它在 Python 2 中创建一个新样式的类(在 Python 3 中,所有类都是新样式)。


[i for i in range(max_year)] 可以绕过这个限制的原因是,从range(max_year) 创建了一个迭代器,然后将其作为参数传递给运行列表推导的临时函数。所以相当于这段代码:

class Student(object):
    max_year = 18
    def _listcomp(iterator):
        result = []
        for i in iterator:
            result.append(i)
        return result

    year_choice = _listcomp(iter(range(max_year + 1, 100)))
    del _listcomp

    def __init__(self, name):
        self.name = name
        print(self.year_choice)

Student('Blah')

非常感谢 Antti Haapala 的解释。

【讨论】:

  • 感谢您的回答,它帮助我解决了这个确切的问题。如果您有理由将类变量保持原样,则可以通过使用第二层列表理解并将所需的类变量包装在元组中以强制它们进入范围,以更一般的方式解决这种情况,就像这样:year_choice = [[i for i in d[0] if i &gt; d[1]] for d in (range(100), max_year)][0]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-26
  • 1970-01-01
  • 2022-12-31
  • 2012-06-06
  • 2015-11-15
  • 2018-09-02
  • 2010-12-01
相关资源
最近更新 更多