osdir.com

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Question about implementing immutability


Iwo Herka <iwoherka at gmail.com> writes:

> Let's say I want to implement immutability for user-defined class.
> More precisely, a class that can be modified only in its (or its
> super-class') __init__ method. My initial idea was to do it the
> following fashion:
>
>     def __setattr__(self, *args, **kwargs):
>         if sys._getframe(1).f_code.co_name == '__init__':
>             return super().__setattr__(*args, **kwargs)
>         raise AttributeError()
>
> What do you think of this approach? Is there a better one?

This is not easy to achieve in Python:
Most instances of classes store their attributes in the
so called "instance dict", accessed via "<i>.__dict__".
Thus, usually, it is quite easy to avoid `__setattr__":
just modify the instance dict directly.

If your aim is to ensure that "normal use" will not
inadvertantly modify your instance
(not to enforce immutability even facing people ready to use implementation
details to break it),
then you might consider the use of a so called "metaclass".
The "metaclass" is responsible to create the class object;
thus, it can modify (add or delete) methods for the class to be constructed.

The main problem in your task is to recognize when
the initial "__init__" call has finished. Your approach
above does not work, should "__init__" call other functions or methods.
With a metaclass, you could ensure, that all "__init__" methods
for classes constructed by it are instrumented.
The instrumentation would ensure that the initial "__init__" call
sets an instance attribute (say) "_initializing_" (to itself)
(using implementation details) at its beginning and remove it at the end.
This way, a "__setattr__" has an easy way to recognize the initialization
phase. The approach above, would not work for recursively called
"__init__" methods - but hopefully, such "__init__" implementations
are extremely rare.