So, the first replies here echo the concerns I stated on the ideas thread, and which are not as of yet addressed.
TL;DR: the freezing of an instance’s class and all its hierarchy (superclasses, metaclasses) seens to be too over-reaching, and may not only cause severe problems as stated by @steve.dower , @ZeroIntensity and @BrenBarn posts (so far).
So, for Python since its inception up to know, if I create a class - I can come “later” and change it - by replacing a method, or changing an attribute. That may or not be “good practice”, but it is inherently allowed in Python.
And with this feature as currently stated, if between the two events, an instance of my class happens to be in a frozen graph, all of a suden an exception will be raised - in my code.
To keep it visual, let’s say we have these:
Project A: my project, project B: another 3rd party lib which makes use of freeze
and Project C: a “final user” project consuming both A and C
Project C comes to add an instance of A.Klass() to a list - and pass that list to B.communicate()
which freezes it, and therefore freezes A.Klass.
The remedy with the PEP as is is to mark A.Klass as “NotFreezable” - which could be feasible (more like a workaround) for new code - but what for already existing code?
What if the “not good programming practice” A.Klass mutation is restricted to a new subclass being created?
The PEP text supposedly addresses this, but doesn’t feel like complete:
Summary
Because this reference [to subclasses] does not get exposed to the programmer in any dangerous way, we permit immutable classes to be subclassed (by mutable classes).
What is “any dangerous way”? The fact is that type(A).__subclassess__()
would allow one to retrieve a “non-frozen” subclass of a frozen class: there is no semantic difference if __subclasses__
were a direct attribute instead of being a callable.
If the protocol will resort to raise an error when super(None, type(self).__subclasses__()[0]).mutate_class()
it could as well just raise the same error when calling self.mutate_class()
-
Maybe assuming that classes are not freezable at all is a worth (and required) compromise on the non-strict immutability side.
Other than rejecting class freezing outright, I’d pursue the proxy idea: That is, instead of eagerly freezing everything in a subject graph, guard the attribute/item retrievals on a frozen object so that nested items are either lazily frozen, or retrieved as a “frozen proxy” (which seems suitable for classes) - in this scenario, a 'frozen proxy" for a class would have the same guards.
(the original object owner could be able to bypass that by having a reference to an item in an object graph prior to freezing, ang them mutating that branch after freezing the root object - but, I think for cases like this “consenting adults” apply)