On Wed, 2 Dec 2020 10:42:25 +1100 Steven D'Aprano email@example.com wrote:
Below is an example of strict mode program with comments explaining various features:
import mod # Leads to a warning: replacing (monkey-patching) a constant slot (function) with a variable. mod.func1 = 1
(Aside: the literal `1` is not a variable, and variables aren't first-class citizens in Python. We can't bind "a variable" to a name, only the variable's value.)
Nobody calls literal "1" a variable. That example explores the meaning of:
def func1(): pass
func1 = 1
And from a PoV of a human programmer, it's like written in the comment - first, the symbol (name) "func1" is defined as a function, and then it's redefined as a variable.
Is monkey-patching disallowed because `mod.func1` is defined as a constant?
What "disallowed" do you mean? The example above clearly says "Leads to a warning". At "run-time" (i.e. eventually, after you've done the monkey-patching), it's disallowed, yes.
Or are all functions automatically considered to be constants?
That's the case, right.
If the later, then decorators won't work in strict mode:
@decorate def func(): ...
is defined as:
# define-decorate-replace def func(): ... func = decorate(func)
That's "conceptual model". How it's actually implemented is, "bu abuse of notation" is:
def decorate(func): ...
Works without hitch with the strict mode.
so if functions are automatically const, decorators won't work.
Occasionally I find that decorator syntax is not sufficient, and I've used the explicit "define-decorate-replace" form. That won't work either.
You'll get a warning, and if you're smarter than the strict mode in that case, you can disable it (in the same sense that you can disable any warning in Python).
Is it only *monkey-patching* from outside of the module that is disallowed, or any rebindings to functions, including within the owning module itself?
In the *run-time* (aka "eventually") any "assign" operation on a const name is disallowed, and additionally adding any new names is disallowed, ditto for deletions.
At the *import-time*, everything is allowed.
If the later, that's also going to break the common idiom:
def func(): # Slow Python version. # maybe replace with fast C version from c_accelerators import *
under strict mode.
Will lead to a warning. Got bitten by that myself, yeah. (I llllove "from foo import *"). Had to finally implement __all__ in Pycopy due to those strict mode problems, can you believe?
Anyway, re: example above would either need to mend your your ways (e.g. run "import *" first then conditionally define missing funcs), or disable the warning.
[Have to stop here, will look at the rest later, thanks for comments.]