【问题标题】:Why is a NameError raised when using a list compression at the class level? [duplicate]为什么在类级别使用列表压缩时会引发 NameError? [复制]
【发布时间】:2019-01-12 10:04:43
【问题描述】:

我的代码中的麻烦对我来说很有趣。

...
class Planet:
    ATMOSPHERE_GASES = {
        'N2':(67.59, 28.0134),
        'O2':(28.04, 31.9988),
        'CO2':(0.0114, 44.00995),
        'CH4':(0.00015, 16.04303),
        'Ar':(1.105, 39.948),
        'Ne':(1.003, 20.179),
        'He':(0.719, 4.0026),
        'Kr':(0.45, 83.80),
        'H2':(0.001, 2.01594),
        'Xe':(0.23, 131.30)}
    ATMOSPHERE_GASES['Other'] = tuple([100-sum([x[0] for x in ATMOSPHERE_GASES.values()]), sum([x[1] for x in ATMOSPHERE_GASES.values()])/len(ATMOSPHERE_GASES.values())])
    ATMOSPHERE_GASES_MOLAR_MASS = sum([sum(ATMOSPHERE_GASES[x]) for x in ATMOSPHERE_GASES.keys()])/100
    ...

ATMOSPHERE_GASES_MOLAR_MASS 报错NameError("name 'ATMOSPHERE_GASES' is not defined",)我检查了缩进块,制表符和其他一些原因,尝试重写这部分,但没有。但是没有类它可以工作!

Traceback (most recent call last):
  File "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft\Python\Core\ptvsd_launcher.py", line 111, in <module>
    vspd.debug(filename, port_num, debug_id, debug_options, run_as)
  File "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft\Python\Core\Packages\ptvsd\debugger.py", line 36, in debug
    run(address, filename, *args, **kwargs)
  File "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft\Python\Core\Packages\ptvsd\_main.py", line 47, in run_file
    run(argv, addr, **kwargs)
  File "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft\Python\Core\Packages\ptvsd\_main.py", line 98, in _run
    _pydevd.main()
  File "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft\Python\Core\Packages\ptvsd\pydevd\pydevd.py", line 1628, in main
    globals = debugger.run(setup['file'], None, None, is_module)
  File "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft\Python\Core\Packages\ptvsd\pydevd\pydevd.py", line 1035, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft\Python\Core\Packages\ptvsd\pydevd\_pydev_imps\_pydev_execfile.py", line 25, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "C:\Users\Rain0Ash\source\repos\A\A\A.py", line 57, in <module>
    class Planet:
  File "C:\Users\Rain0Ash\source\repos\A\A\A.py", line 70, in Planet
    ATMOSPHERE_GASES_MOLAR_MASS = sum([mult(ATMOSPHERE_GASES[x]) for x in ATMOSPHERE_GASES.keys()])/100
  File "C:\Users\Rain0Ash\source\repos\A\A\A.py", line 70, in <listcomp>
    ATMOSPHERE_GASES_MOLAR_MASS = sum([mult(ATMOSPHERE_GASES[x]) for x in ATMOSPHERE_GASES.keys()])/100
NameError: name 'ATMOSPHERE_GASES' is not defined

【问题讨论】:

  • P.S.我在 python 方面不是很强大,但它可以使用通常的变量 - 在这个例子中使用 dict - 不。
  • 这里没有说明错误发生的原因,但可以简化计算:ATMOSPHERE_GASES_MOLAR_MASS = sum(sum(tup) for tup in ATMOSPHERE_GASES.values()) / 100
  • 谢谢。看来已经解决了,明天我会检查。
  • @Rain0Ash 好的,我的回答怎么样?
  • 它有效,但它需要对象,在这种情况下不适合我。我使用以前的代码,它解决了这个问题。 Edward Minnix 给了我一个解释。不过谢谢。

标签: python python-3.x function class variables


【解决方案1】:

这是一个范围界定问题。 Python 中的类级变量总是有点奇怪,但如果你大量使用它们,了解它的工作原理很重要。

实际问题在于列表推导,它试图将ATMOSPHERE_GASES 作为全局变量访问。要了解原因,您需要了解更多关于由解释器构建的类。要找到问题,我们首先需要使用ast。这将允许获得 抽象语法树,它是一种数据结构。然后我们就可以使用内置的compile函数将其转换为字节码对象。

 tree = ast.parse('''
 <your code here...>
 ''')
 # You need a filename, so we will just use __name__ because it's not important for our purpose
 # The 'exec' is to tell the interpreter we want to execute a module, not evaluate an expression
 code = compile(tree, __name__, 'exec')

然后我们需要使用 Python 反汇编模块dis。函数dis.dis 将让你看到代码编译成什么。它会打印出很多代码,但我会将其限制为与列表推导相关的代码(列表推导和生成器表达式有自己的代码对象,如函数和模块)。

 >>> dis.dis(code)
 ...
 Disassembly of <code object <listcomp> at 0x00000290AA0D59C0, file "__main__", line 4>:
  4           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                16 (to 22)
              6 STORE_FAST               1 (x)
              8 LOAD_GLOBAL              0 (sum)
             10 LOAD_GLOBAL              1 (ATMOSPHERE_GASES)
             12 LOAD_FAST                1 (x)
             14 BINARY_SUBSCR
             16 CALL_FUNCTION            1
             18 LIST_APPEND              2
             20 JUMP_ABSOLUTE            4
        >>   22 RETURN_VALUE

如果您注意索引 10 处的指令,您会注意到它显示为LOAD_GLOBAL 1 (ATMOSPHERE_GASES)。这意味着它在全局范围内寻找ATMOSPHERE_GASES,而不是在类级别范围内。

ATMOSPHERE_GASES_MOLAR_MASS = sum(sum(tup) for tup in ATMOSPHERE_GASES.values()) / 100 解决方案之所以有效,是因为您可以避免导致问题的ATMOSPHERE_GASES[x] 逻辑。

TL;DR:由于类作用域变量的方式,类级变量很复杂。列表推导将ATMOSPHERE_GASES 视为全局变量,然后在模块的全局范围内找不到它。最好将此计算从类的定义移到全局范围内。

【讨论】:

  • 非常感谢您的解释。我明白这一点。
【解决方案2】:

你可以在你的类中定义__init__,然后做self,如下:

class Planet:
   def __init__(self):
       self.ATMOSPHERE_GASES = {
           'N2':(67.59, 28.0134),
           'O2':(28.04, 31.9988),
           'CO2':(0.0114, 44.00995),
           'CH4':(0.00015, 16.04303),
           'Ar':(1.105, 39.948),
           'Ne':(1.003, 20.179),
           'He':(0.719, 4.0026),
           'Kr':(0.45, 83.80),
           'H2':(0.001, 2.01594),
           'Xe':(0.23, 131.30)}
       self.ATMOSPHERE_GASES['Other'] = tuple([100-sum([x[0] for x in self.ATMOSPHERE_GASES.values()]), sum([x[1] for x in self.ATMOSPHERE_GASES.values()])/len(self.ATMOSPHERE_GASES.values())])
       self.ATMOSPHERE_GASES_MOLAR_MASS = sum([sum(self.ATMOSPHERE_GASES[x]) for x in self.ATMOSPHERE_GASES.keys()])/100

另请参阅:Python __init__ and self what do they do?

【讨论】:

  • 您不需要使用__init__ 来完成此操作。只需使用self.ATMOSPHERE_GASES
  • 这有一个问题是每次都重新计算而不是像 Java 中的静态变量一样对待它们(这通常是使用类级变量的动机之一)。此外,这意味着您必须实例化一个 Planet 对象才能使用这些变量而不是 Planet.ATMOSPHERE_GASES
  • @smac89 你仍然需要用某种方法来计算它。您无法计算类主体本身的值。如果您使用此方法,__init__ 是一个使用它的好地方,因为它在实例创建时运行。
  • Ofc!是的,你是对的
猜你喜欢
  • 1970-01-01
  • 2016-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-07
  • 2014-09-01
  • 2016-01-16
  • 1970-01-01
相关资源
最近更新 更多