This recipe is here for a couple of reasons: 1) discourage a common misuse of __slots__; 2) show how to restrict Python dynamism.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # requires Python 2.2+
def frozen(set):
"Raise an error when trying to set an undeclared name."
def set_attr(self,name,value):
if hasattr(self,name):
set(self,name,value)
else:
raise AttributeError("You cannot add attributes to %s" % self)
return set_attr
class Frozen(object):
"""Subclasses of Frozen are frozen, i.e. it is impossibile to add
new attributes to them and their instances."""
__setattr__=frozen(object.__setattr__)
class __metaclass__(type):
__setattr__=frozen(type.__setattr__)
|
__slots__ are a Python 2.2 feature intended as a memory optimization: however, judging from recent posts in c.l.py, lots of people have misunderstood its aim, and think __slots__ is used to introduce declarations in Python. The reason why they think so is that it is impossible to add undeclared run-time attributes to instances of classes with __slots__. This is a limitation of __slots__, not a feature!
Nevertheless there are people who want to restrict Python dynamism, for various reasons. The right way to do it is not via __slots__, but via __setattr__. Here I show a simple recipe - which maybe expanded and customized - to restrict the dynamism of Python classes.
Notice that the recipe inhibits not only the addition of runtime attributes to objects, but even to classes.
Here is an example of usage:
<pre> class Person(Frozen): firstname="" lastname="" def __init__(self,firstname,lastname): self.firstname=firstname self.lastname=lastname
me=Person("Michele","Simionato")
</pre> Using this "feature" one is forced to declare the attributes of a class explicitly since setting an undeclared attribute raises an error:
>>> Person.add_an_attribute="something" # => Attribute Error
>>> me.add_an_attribute="something" # => Attribute Error
Also, the normal Python idiom "self.somename=something" raises an error if "somename" is not explicitely declared in the class. In other words, subclasses of "Frozen" behaves more similarly to Java/C++ classes, so this limitation may be useful in the coding of prototypes to be converted in static languages.
Download
Copy to clipboard
Getting rid of the instance and class level variable dependency... This is a very useful recipe, but it has one large wart...
In order to define an instance variables, one must also define a class (static) variable of the same name, and vice versa.
A more pythonic solution would be adjust the frozen function to only allow attributes to be set when either:
1) They already exist
2) They are being set from within an __init__ method of either the Frozen class, or a derived class.
This way, we are providing the python equivalent to C++ and Java's declarations, but we are doing it in a pythonic way, in the __init__ method of our object. After all, python objects do not have declarations, nor should they...
In order to do this, we need to alter the freeze function as follows:
Here are a few examples of usage with the modified code
Warmest Regards,
Michael Loritsch