How To Forbid Creation Of New Class Attributes In Python?
Solution 1:
I think you should play with metaclasses. It can define the behavior of your class instead of its instances.
The comment from Patrick Haugh refers to another SO answer with the following code snippet:
classFrozenMeta(type):
def__new__(cls, name, bases, dct):
inst = super().__new__(cls, name, bases, {"_FrozenMeta__frozen": False, **dct})
inst.__frozen = Truereturn inst
def__setattr__(self, key, value):
if self.__frozen andnothasattr(self, key):
raise TypeError("I am frozen")
super().__setattr__(key, value)
classA(metaclass=FrozenMeta):
a = 1
b = 2
A.a = 2
A.c = 1# TypeError: I am frozenSolution 2:
@AlexisBRENON's answer works but if you want to emulate the behavior of a built-in class, where subclasses are allowed to override attributes, you can set the __frozen attribute to True only when the bases argument is empty:
classFrozenMeta(type):
def__new__(cls, name, bases, dct):
inst = super().__new__(cls, name, bases, {"_FrozenMeta__frozen": False, **dct})
inst.__frozen = not bases
return inst
def__setattr__(self, key, value):
if self.__frozen andnothasattr(self, key):
raise TypeError("I am frozen")
super().__setattr__(key, value)
classA(metaclass=FrozenMeta):
a = 1
b = 2classB(A):
pass
B.a = 2
B.c = 1# this is OK
A.c = 1# TypeError: I am frozenSolution 3:
Whenever you see built-in/extension type you are dealing with an object that was not created in Python. The built-in types of CPython were created with C, for example, and so the extra behavior of assigning new attributes was simply not written in.
You see similar behavior with __slots__:
>>>classHuh:... __slots__ = ('a', 'b')>>>classHah(Huh):...pass>>>Huh().c = 5# traceback>>>Hah().c = 5# worksAs far as making Python classes immutable, or at least unable to have new attributes defined, a metaclass is the route to go -- although anything written in pure Python will be modifiable, it's just a matter of how much effort it will take:
>>>classA(metaclass=FrozenMeta):... a = 1... b = 2>>>type.__setattr__(A, 'c', 9)>>>A.c
9
A more complete metaclass:
classLocked(type):
"support various levels of immutability"#def__new__(metacls, cls_name, bases, clsdict, create=False, change=False, delete=False):
cls = super().__new__(metacls, cls_name, bases, {
"_Locked__create": True,
"_Locked__change": True,
"_Locked__delete": True,
**clsdict,
})
cls.__create = create
cls.__change = change
cls.__delete = delete
return cls
#def__setattr__(cls, name, value):
ifhasattr(cls, name):
if cls.__change:
super().__setattr__(name, value)
else:
raise TypeError('%s: cannot modify %r' % (cls.__name__, name))
elif cls.__create:
super().__setattr__(name, value)
else:
raise TypeError('%s: cannot create %r' % (cls.__name__, name))
#def__delattr__(cls, name):
ifnothasattr(cls, name):
raise AttributeError('%s: %r does not exist' % (cls.__name__, name))
ifnot cls.__delete or name in (
'_Locked__create', '_Locked__change', '_Locked_delete',
):
raise TypeError('%s: cannot delete %r' % (cls.__name__, name))
super().__delattr__(name)
and in use:
>>>classChangable(metaclass=Locked, change=True):... a = 1... b = 2...>>>Changable.a = 9>>>Changable.c = 7
Traceback (most recent call last):
...
TypeError: Changable: cannot create 'c'
>>>del Changable.b
Traceback (most recent call last):
...
TypeError: Changable: cannot delete 'b'
Post a Comment for "How To Forbid Creation Of New Class Attributes In Python?"