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)
    try:
        # body of the function
        ...
    finally:
       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__?

Example:

def func():
    print(func.a)
func.a = 1

Usage:

 >>> func()
 1

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():
    print(a)
    print(b)
func.b = 2

Usage:

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

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

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

---
Ricky.

"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
UnboundLocalError:


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


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



--
Steve

 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. 

Example:

@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