3

I get interesting for me trouble in my code.

...
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 give error NameError("name 'ATMOSPHERE_GASES' is not defined",) I check indented block, tabs and some other reasons, try to rewrite this part, but nothing. But without class it works!

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
FMc
  • 41,963
  • 13
  • 79
  • 132
  • P.S. I'm not very strong in the python, but with the usual variables it works - with the dict in this example - no. –  Aug 05 '18 at 01:49
  • 1
    This does not address why the error occurs, but you can simplify the calculation: `ATMOSPHERE_GASES_MOLAR_MASS = sum(sum(tup) for tup in ATMOSPHERE_GASES.values()) / 100`. – FMc Aug 05 '18 at 01:56
  • Thanks. And it seems fix, tomorrow I'll check. –  Aug 05 '18 at 02:03
  • @Rain0Ash Ok, How about my answer? – U13-Forward Aug 05 '18 at 02:08
  • It works, but it need object, that doesn't suit me in this case. I use previous code, it fix this problem. And Edward Minnix gave me an explanation. But thanks. –  Aug 05 '18 at 07:41

2 Answers2

2

You can define __init__ in your class, and then do self, like below:

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

See also: Python __init__ and self what do they do?

U13-Forward
  • 69,221
  • 14
  • 89
  • 114
  • You don't need to use `__init__` to accomplish this. Simply use `self.ATMOSPHERE_GASES` – smac89 Aug 05 '18 at 02:23
  • This has the problem of recomputing this every time rather than treating them like static variables in Java (which is usually one of the motivations for using class-level variables). Also, this means you must instantiate a `Planet` object in order to use these variables instead of `Planet.ATMOSPHERE_GASES` – Edward Minnix Aug 05 '18 at 02:23
  • 1
    @smac89 you would still need to calculate this in some method. You can't compute the value in the class body itself. If you use this method, `__init__` is a fine place to use it because it is run on instance creation. – Edward Minnix Aug 05 '18 at 02:25
  • Ofc! Yea you're right – smac89 Aug 05 '18 at 02:26
1

This is a scoping issue. Class level variables in Python can always be a little strange, but it is important to understand how it works if you use them heavily.

The actual issue is with the list comprehension, which tries to access ATMOSPHERE_GASES as a global variable. To see why, you need to understand more about classes are built by the interpreter. To find the issue, we will first need to use the ast. This will allow to get the abstract syntax tree, which is a data structure. Then we will be able to convert it into a byte code object using the builtin compile function.

 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')

Then we will need to use the Python disassembly module dis. The function dis.dis will let you see what the code compiles to. It prints out a lot of code, but I will limit it to the code associated with the list comprehension (list comprehensions and generator expressions have their own code objects like functions and modules).

 >>> 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

If you pay attention to the instruction at index 10, you will notice that it says LOAD_GLOBAL 1 (ATMOSPHERE_GASES). This means it is looking for ATMOSPHERE_GASES in the global scope, not the class-level scope.

The reason the ATMOSPHERE_GASES_MOLAR_MASS = sum(sum(tup) for tup in ATMOSPHERE_GASES.values()) / 100 solution works is because then you can avoid the ATMOSPHERE_GASES[x] logic, which is what is causing the problem.

TL;DR: Class level variables are complicated because of how classes scope variables. The list comprehension considers ATMOSPHERE_GASES a global variable, and then can't find it in the module's global scope. It would most likely be best to move this computation outside of the class's definition into the global scope.

Edward Minnix
  • 2,889
  • 1
  • 13
  • 26