On Thu, May 27, 2021 at 8:19 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, May 27, 2021 at 07:56:16AM -0000, Shreyan Avigyan wrote:

> This idea proposes to add a keyword
> (static, maybe?) that can create static variables that can persist
> throughout the program yet only accessible through the function they
> are declared and initialized in.

Here is a sketch of how this could work, given a function like this:

    def func(arg):
        static spam, eggs
        static cheese = expression

At function declaration time, the two static statements tell the
compiler to:

* treat spam, eggs and cheese as local variables (use LOAD_FAST instead
  of LOAD_GLOBAL for lookups);

* allocate static storage for them using the same (or similar) mechanism
  used for function default values;

* spam and eggs get initialised as None;

* cheese gets initialised to the value of `expression`, evaluated
  at function declaration time just as default arguments are.

When the function is called:

* the interpreter automatically initialises the static variables
  with the stored values;

* when the function exits (whether by return or by raising an
  exception) the static storage will be updated with the current
  values of the variables.

As a sketch of one possible implementation, the body of the function
represented by ellipsis `...` might be transformed to this:

    # initialise statics
    spam = LOAD_STATIC(0)
    eggs = LOAD_STATIC(1)
    cheese = LOAD_STATIC(2)
        # body of the function
       STORE_STATIC(spam, 0)
       STORE_STATIC(eggs, 1)
       STORE_STATIC(cheese, 2)

Couldn't you already get pretty close to this by attaching your static values to the function __dict__?


def func():
func.a = 1


 >>> func()

Of course that is slower because there is an attribute lookup.

But could there be a decorator that links the function __dict__ to locals(), so they are intertwined? 

@staticify({ 'a':1})
def func():
func.b = 2


 >>> func()
 >>> func.a = 3  # dynamic update of func.__dict__
 >>> func()

The locals dict in the function body would look something like this:

ChainMap(locals(), {'a':1})


"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
One subtlety: what if the body of the function executes `del spam`? No
problem: the spam variable will become undefined on the next function
call, which means that subsequent attempts to get its value will raise

        x = spam + 1
    except UnboundLocalError:
        spam = 0
        x = 1

I would use this static feature if it existed. +1


 Same thing would happen with my idea: del a would delete a from the func.__dict__ (just like with ChainMap). But if you add it back again later, it would not be static anymore. 


@staticify('a': 1)
def func():
    print(a)  # fast static lookup
    del a  # static is deleted
    a = 2  # this is local now
    func.b = 3  # but this is a static