How does _SOME_CONSTANT = const(2) work in a class?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
martincho
Posts: 96
Joined: Mon May 16, 2022 9:59 pm

How does _SOME_CONSTANT = const(2) work in a class?

Post by martincho » Mon Jun 13, 2022 4:52 pm

https://docs.micropython.org/en/latest/ ... thon.const

The standard use is clear, you declare your constants and the parser runs a text substitution and does not allocate a variable.

How about in a class?

Code: Select all

class ClassA():
    _CONST_1 = const(1)
    _CONST_2 = const(2)
    
    def __init__(self):
    	self.some_property_1 = self._CONST_1
    	self.some_property_2 = self._CONST_2
Does the parser still take "self._CONST_1" and replace it with the value without allocating an object or running any code to get the value? In other words, text substitution.

Is the underscore necessary to keep it from global scope? This is easy to test, just haven't done it yet. Wondering what the recommendation might be in this regard.

Thanks.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: How does _SOME_CONSTANT = const(2) work in a class?

Post by pythoncoder » Tue Jun 14, 2022 9:07 am

I don't know the answer to your main question and have often wondered about this myself. My guess is that text substitution still takes place but how to test this?

The purpose of the underscore in const declarations with global scope is this. A variable of form

Code: Select all

FOO=42
may potentially be accessed from another module:

Code: Select all

from foo_module import *
a = FOO
Consequently the compiler, when compiling foo_module, must allocate RAM for the variable FOO and assign 42 to it, even though it performs text substitutions elsewhere in the module. In Python variables with a leading underscore are not imported if you issue

Code: Select all

from foo_module import *
So using such names saves RAM.
Peter Hinch
Index to my micropython libraries.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: How does _SOME_CONSTANT = const(2) work in a class?

Post by pythoncoder » Tue Jun 14, 2022 9:39 am

OK I've figured out how to answer the main question. Issuing

Code: Select all

micropython -v -v module.py
causes micropython to emit the bytecode. I did this with these two versions:

Code: Select all

class ClassA():
    CONST_1 = const(1)
    
    def __init__(self):
    	self.some_property_1 = self.CONST_1  # Fails here with leading _: no attribute

a = ClassA()
and

Code: Select all

class ClassA():
    CONST_1 = 1 #const(1)
    
    def __init__(self):
    	self.some_property_1 = self.CONST_1  # Fails here with leading _: no attribute

a = ClassA()
Both produced the same bytecode. See my comments on the relevant lines:

Code: Select all

Raw bytecode (code_info_size=5, bytecode_size=17):
 10 06 01 69 60 54 32 00 10 02 34 02 16 02 11 02
 34 00 16 03 51 63
arg names:
(N_STATE 3)
(N_EXC_STACK 0)
  bc=0 line=1
  bc=9 line=4
  bc=9 line=7
00 LOAD_BUILD_CLASS
01 MAKE_FUNCTION 7f356daa5460
03 LOAD_CONST_STRING 'ClassA'
05 CALL_FUNCTION n=2 nkw=0
07 STORE_NAME ClassA
09 LOAD_NAME ClassA
11 CALL_FUNCTION n=0 nkw=0
13 STORE_NAME a
15 LOAD_CONST_NONE
16 RETURN_VALUE
File rats23.py, code block 'ClassA' (descriptor: 7f356daa5460, bytecode @7f356daa5680 22 bytes)
Raw bytecode (code_info_size=5, bytecode_size=17):
 00 06 02 28 43 11 04 16 05 10 02 16 06 81 16 07
 32 00 16 08 51 63
arg names:
(N_STATE 1)
(N_EXC_STACK 0)
  bc=0 line=1
  bc=8 line=2
  bc=11 line=4
00 LOAD_NAME __name__
02 STORE_NAME __module__
04 LOAD_CONST_STRING 'ClassA'
06 STORE_NAME __qualname__
08 LOAD_CONST_SMALL_INT 1   # Storing 1 to class variable
09 STORE_NAME CONST_1
11 MAKE_FUNCTION 7f356daa5220
13 STORE_NAME __init__
15 LOAD_CONST_NONE
16 RETURN_VALUE
File rats23.py, code block '__init__' (descriptor: 7f356daa5220, bytecode @7f356daa56a0 14 bytes)
Raw bytecode (code_info_size=6, bytecode_size=8):
 11 08 08 09 60 20 b0 13 07 b0 18 0a 51 63
arg names: self
(N_STATE 3)
(N_EXC_STACK 0)
  bc=0 line=1
  bc=0 line=4
  bc=0 line=5
00 LOAD_FAST 0
01 LOAD_ATTR CONST_1   # Explicit load of class variable
03 LOAD_FAST 0
04 STORE_ATTR some_property_1
06 LOAD_CONST_NONE
07 RETURN_VALUE
Note that a version with a leading underscore fails to compile: the class variable is not visible to __init__().
Peter Hinch
Index to my micropython libraries.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: How does _SOME_CONSTANT = const(2) work in a class?

Post by jimmo » Mon Jun 20, 2022 5:27 am

martincho wrote:
Mon Jun 13, 2022 4:52 pm
In other words, text substitution.
pythoncoder wrote:
Tue Jun 14, 2022 9:39 am
Note that a version with a leading underscore fails to compile
Yes, exactly what pythoncoder said.

I think this behaviour is a bit confusing, and furthermore as there is no scope handling, as this demo shows:

Code: Select all

class ClassA():
    _CONST_1 = const(1)

    def __init__(self):
        print(self._CONST_1)

# a = ClassA() -- won't work (but probably should): 'ClassA' object has no attribute '_CONST_1'
print(_CONST_1) # Unexpectedly prints `1` (surprising!)
I guess the general advice is not to use const() in class constants, as it offers no benefit in any circumstance (as Peter's disassembly shows, when you write self.x, x will not be understood as a constant). Instead, put all constants at global scope.

martincho
Posts: 96
Joined: Mon May 16, 2022 9:59 pm

Re: How does _SOME_CONSTANT = const(2) work in a class?

Post by martincho » Mon Jun 20, 2022 8:39 am

I get it. It's just that "unclean" feeling of not having everything inside the class definition. Not an issue at all.

Post Reply