Block setting of class attributes (__setattr__)
Question:
- is there a simple way to prevent setting new class attrs?
- while trying with the following snippet, shouldn’t
setattr(Derived, "test1", 1)
call the __setattr__
from Base
?
class Base:
def __setattr__(self, key, value):
raise PermissionError('in base')
def __init_subclass__(cls, *args, **kwargs):
def _setattr_(inst, key, val):
raise PermissionError('in derived')
cls.__setattr__ = _setattr_
class Derived(Base):
pass
setattr(Derived, "test1", 1)
Derived.__setattr__(Derived, "test2", 2)
Traceback (most recent call last):
.
.
.
PermissionError: in derived
Base.__setattr__(Derived, "test3", 3)
Traceback (most recent call last):
.
.
.
PermissionError: in base
EDIT:
this is a duplicate. See below.
Answers:
Not without a metaclass.
The __setattr__
method you define in a class only affects instances of that class (or instances of subclasses), but not subclasses or the class itself (this is true for most methods exlcuding stuff like __new__
and __init_subclass__
).
setattr(cls, attr_name, value)
calls cls.__class__.__setattr__(cls, attr_name, value)
, but Derived.__class__ == type
, not Base
After a better selection of keywords and another round of searches I found this problem solved in this SO answer. I am keeping it here as others might have the same difficulty to find one or the other, hence increasing the chances of getting a match.
Also here’s the solution to the prob (minimal working example):
class Meta(type):
def __setattr__(self, key, value):
raise PermissionError('in meta')
class Base(metaclass=Meta):
def __setattr__(self, key, value):
raise PermissionError('in base')
class Derived(Base):
pass
setattr(Derived, "test1", 1)
.
.
.
PermissionError: in meta
- is there a simple way to prevent setting new class attrs?
- while trying with the following snippet, shouldn’t
setattr(Derived, "test1", 1)
call the__setattr__
fromBase
?
class Base:
def __setattr__(self, key, value):
raise PermissionError('in base')
def __init_subclass__(cls, *args, **kwargs):
def _setattr_(inst, key, val):
raise PermissionError('in derived')
cls.__setattr__ = _setattr_
class Derived(Base):
pass
setattr(Derived, "test1", 1)
Derived.__setattr__(Derived, "test2", 2)
Traceback (most recent call last):
.
.
.
PermissionError: in derived
Base.__setattr__(Derived, "test3", 3)
Traceback (most recent call last):
.
.
.
PermissionError: in base
EDIT:
this is a duplicate. See below.
Not without a metaclass.
The __setattr__
method you define in a class only affects instances of that class (or instances of subclasses), but not subclasses or the class itself (this is true for most methods exlcuding stuff like __new__
and __init_subclass__
).
setattr(cls, attr_name, value)
calls cls.__class__.__setattr__(cls, attr_name, value)
, but Derived.__class__ == type
, not Base
After a better selection of keywords and another round of searches I found this problem solved in this SO answer. I am keeping it here as others might have the same difficulty to find one or the other, hence increasing the chances of getting a match.
Also here’s the solution to the prob (minimal working example):
class Meta(type):
def __setattr__(self, key, value):
raise PermissionError('in meta')
class Base(metaclass=Meta):
def __setattr__(self, key, value):
raise PermissionError('in base')
class Derived(Base):
pass
setattr(Derived, "test1", 1)
.
.
.
PermissionError: in meta