Reputation: 540
I just realised that __setattr__
doesn't work on the class itself. So this implementation,
class Integer:
me_is_int = 0
def __setattr__(self, name, value):
if not isinstance(value, int):
raise TypeError
doesn't raise on this:
Integer.me_is_int = "lol"
So, switching to a metaclass:
class IntegerMeta:
def __setattr__(cls, name, value):
if not isinstance(value, int):
raise TypeError
class Integer(metaclass=IntegerMeta):
me_is_int = 0
this works, but this:
Integer().me_is_int = "lol"
doesn't work yet again. So do I need to copy the __setattr__
method in Integer
again to make it work on instances? Is it not possible for Integer
to use IntegerMeta
's __setattr__
for instances?
Upvotes: 0
Views: 123
Reputation: 110271
You are right in your reasoning: having a custom __setattr__
special method in the metaclass will affect any value setting on the class, and having the it on the class will affect all instances of the class.
With that in mind, if you don't want to duplicate code, is to arrange the metaclass itself to inject the logic in a class, whenever it is created.
The way you've written it, even thinking as an example, is dangerous, as it will affect any attribute set on the class or instances - but if you have a list of the attributes you want to guard in that way, it would also work.
attributes_to_guard = {"me_is_int",}
class Meta:
def __init__(cls, name, bases, ns, **kw):
# This line itself would not work if the setattr would not check
# for a restricted set of attributes to guard:
cls.__setattr__ = cls.__class__.__setattr__
# Also, note that this overrides any manually customized
# __setattr__ on the classes. The mechanism to call those,
# and still add the guarding logic in the metaclass would be
# more complicated, but it can be done
super().__init__(name, bases, ns, **kw)
def __setattr__(self, name, value):
if name in attributes_to_guard not isinstance(value, int):
raise TypeError()
class Integer(metaclass=Meta):
me_is_int = 0
Upvotes: 1