On Thu, May 27, 2021 at 11:21:26PM +1000, Chris Angelico wrote: > On Thu, May 27, 2021 at 10:20 PM Steven D'Aprano <st...@pearwood.info> wrote: > > 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); > > I don't think LOAD_FAST would be suitable here - isn't it always going > to look in the stack frame?
Where else should it look? I'll admit I'm not an expert on the various LOAD_* bytecodes, but I'm pretty sure LOAD_FAST is used for local variables. Am I wrong? My idea is that the static variable should be a local variable that gets saved on function exit and restored on function entry. Is there another concept of static variables that I should know about? > > * allocate static storage for them using the same (or similar) mechanism > > used for function default values; > > Default values are attached to the function object (in either the > __defaults__ tuple or the __kwdefaults__ dict). Right. In principle we could just shove the static values in the __defaults__ tuple, but it's probably better to use a distinct __statics__ dunder. > > * 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. > > Hmm, I see what you mean. Not sure that this is really necessary > though - If you don't store the values away somewhere on function exit, how do you expect them to persist from one call to the next? Remember that they are fundamentally variables -- they should be able to vary from one call to the next. > and it could cause extremely confusing results with > threading. Don't we have the same issues with globals, function attributes, and instance attributes? I'm okay with saying that if you use static *variables* (i.e. they change their value from one call to the next) they won't be thread-safe. As opposed to static "constants" that are only read, never written. But if you have a suggestion for a thread-safe way for functions to keep variables alive from one call to the next, that doesn't suffer a big performance hit, I'm all ears :-) > Agreed, I'd use it too. But I'd define the semantics slightly differently: > > * If there's an expression given, evaluate that when the 'def' > statement is executed, same as default args That's what I said, except I called it function definition time :-) > * Otherwise it'll be uninitialized, or None, bikeshedding opportunity, have > fun I decided on initialising them to None because it is more convenient to write: if my_static_var is None: # First time, expensive computation. ... than the alternative with catching an exception. YMMV. > * Usage of this name uses a dedicated LOAD_STATIC or STORE_STATIC bytecode > * The values of the statics are stored in some sort of > high-performance cell collection, indexed numerically Isn't that what LOAD_FAST already does? > It would be acceptable to store statics in a dict, too, but I suspect > that that would negate some or all of the performance advantages. > Whichever way, though, it should ideally be introspectable via a > dunder attribute on the function. Maybe I'm misunderstanding you, or you me. Let me explain further what I think can happen. When the function is being executed, I think that static variables should be treated as local variables. Why build a whole second implementation for fast cell-based variables, with a separate set of bytecodes, to do exactly what locals and LOAD_FAST does? We should reuse the existing fast local variable machinery, not duplicate it. The only part that is different is that those static locals have to be automatically initialised on function entry (like parameters are), and then on function exit their values have to be stored away in a dunder so they aren't lost (as plain local variables are lost when the function exists). That bit is new. We already have a mechanism to initialise locals: it's used for default values. The persistent data is retrieved from the appropriate dunder on the function and bound to the local variable (parameter). We can do the same thing. We will probably use a different dunder. That doesn't mean that every single access to the local static variable needs to be retrieved from the dunder, that would likely be slow. Only on function entry. The difference between function default arguments and statics is that if you rebind the parameter, that new value doesn't get written out to the __defaults__ dunder on function exit. But for statics, it should be. Are we on the same page here? > Semantically, this would be very similar to writing code like this: > > def count(): > THIS_FUNCTION.__statics__["n"] += 1 > return THIS_FUNCTION.__statics__["n"] > count.__statics__ = {"n": 1} > > except that it'd be more optimized (and wouldn't require magic to get > a function self-reference). The most obvious optimization is that you only write the static value out to the dunder on function exit, not on ever rebinding operation. > Note that the statics *must* be defined on the function, NOT on the > code object. Just like function defaults, they need to be associated > with individual instances of a function. Absolutely. -- Steve _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/TNTZ36L56HMTIZ2IP5VK6TKOMWH2OHT5/ Code of Conduct: http://python.org/psf/codeofconduct/