tl;dr What if Python had a mutable clone object which changes how the object works without changing its class level attributes.
Hello People!
(Backstory) Recently I have been working on creating a custom compiler for some custom markup / logic language I am working on. To do so in native Python, it gets a little tricky, so I decided to create a small library / package to be able to use it in several repositories easily (my project is distributed across 3-4 Python environments).
(Context) One of the major things my package is doing, is adding additional utilities classes, functions and sometimes on objects. These task of adding additional utilities happens during declaration and may / may not affect the actual usage of the things.
(Problem) As much as I am working on making the package great on itās own, working with native immutable classes and objects make it lot harder. I canāt change the behaviour without creating a wrapper, but then I need to manually figure out all the properties and it gets hard and uncertain with all possible attributes.
The best I am able to do is working very cautiously and injecting the objects on mutable wrappers on original immutable entity. Although I am able to achieve what how I want it to behave (kind of), in a lot of cases I want to be able to create a mutation in the real object itself, like changing how a function primarily behave - like adding arg type validation, attached pre-call and post-call functions, and possibly more utilities.
I would suggest that having mutable types increase convenience, it would be a nice to have, but at the same time I understand that this makes code inconsistent and unsafe at times. For example, someone will be able to infect code, possibly with malicious content. This is a major plus for having immutable entities (at least primitives like int, str, function, etc).
Here is what Iām proposing, we could possibly have a function inside object / external function that can convert the entity to a safe mutable copy. Accessing, updating or working with this copy and handling it is now very convenient, while the trade off being the security and proper usage is now expected in the hands of developer using the mutable copy. The original immutable entity will remain safe and preserve its natural behavour as it was intended to be.
Code-wise, something like this should give clarity:
hello_world: str = "Hello, World!"
hello_world.to_mutable_copy() # changes the variable to be a mutable copy now
hello_world.__call__ = lambda self: print(self) # gives error in normal case, but not here because it will now become a mutable copy.
hello_world() # prints "Hello, World!"
The example given is super simplified and I donāt encourage others to use the feature like this. At the same time I canāt really define how the Python developer community will use it, but I think having such thing might help developers working on projects and libraries.
For types wise, I think something like the following code will be feasible.
hello: str = "hello" # original type: str
hello_mutable: mutable<str> = "hello".to_mutable_copy() # internally converts to a type "mutable", with args (generics) of (str,).
My library primarily does work-arounds for function cases right now using decorators. It took me 3-4 iterations and will possibly take more to easily handle this task. I handle it by creating a wrapper function before using the actual injection to modify attributes and calling properties. The bounded functions are also handled in a similar fashion during class instantiation, where instead of directly using the function, bounded function is being handled.
Thatās all what I wanted to say for now. Would love to know what the community thinks of this, how people would intend to use it if implemented, if people actually want this to be implemented or not, how people are working around this in their projects and packages, should this be converted to a PEP, and other aspects and views about this topic.
EDIT 1: I mentioned str for simplicity, my actual use case is for function types but thought it would be interesting and helpful to know for more generic.
EDIT 2: I started this thread to discuss the idea about having āmutable clonesā of objects that change class level behaviours. In my codebase, I am not using this and have work-arounds which I donāt intend to change unless I find something solid.
EDIT 3: Added tl;dr + this update.
Thanks,
AttAditya