On Wed, Dec 02, 2020 at 10:52:30AM +0300, Paul Sokolovsky wrote:
# Leads to a warning: replacing (monkey-patching) a constant slot (function) with a variable. mod.func1 = 1
That example explores the meaning of:
def func1(): pass
func1 = 1
Those two code snippets demonstrate different things. Your original example demonstrates assigning to an module attribute from outside of the module. Your new example demonstrates assigning to a global variable from inside the same module. These call different byte-codes in CPython (STORE_ATTR and STORE_NAME).
There is, as far as I know, no way to hook into and prevent STORE_NAME as yet. But STORE_ATTR calls are easy to override:
... def __setattr__(self, name, value): ... print("You tried to change an module attribute, you naughty person!") ...
glob.__class__ = DemoModule
glob.spam = 1
You tried to change an module attribute, you naughty person!
Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: module 'glob' has no attribute 'spam' ```
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.
It sounds like you are describing a programmer who doesn't understand Python's execution model.
Do you understand that "func1" is already a variable? In the sense that "variables" are name bindings, which is the only sense that applies to Python. Just because it is bound to a function object doesn't make it something different -- "func1" is still a name bound to a value, which is the Python concept of a variable.
Paul, I would have more confidence in your proposal if you described your proposal in terms of Python's existing execution model. Because so far, the language you use to describe your proposal sounds like the language used by somebody who doesn't understand Python's execution model.
That does not give me confidence in your understanding of the consequences of these changes, or that you are coming to this proposal as somebody who loves Python. If you want to program in another language with radically different semantics, there are hundreds of languages that aren't Python. You don't have to turn Python into one of them.
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.
What disallowed do I mean? Exactly the same disallowed you just agreed with.
Or are all functions automatically considered to be constants?
That's the case, right.
Okay, so all functions are automatically constants.
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.
Okay, Chris made the same point.
Is that a language guarantee or a quirk of the implementation?
For this to be guarenteed to work requires the implementation of decorators to be guaranteed by the language. If it is not already a guarantee, it will have to be made one.
(For the record, that is not a point against your proposal, just an observation.)
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).
So "strict mode" isn't actually strict, it's just a built-in linter.