I trying to define a Python metaclass to limit the number of attributes that a class may contain, starting at creation time. I am constrained to use Python 3.7 due to system limitations
I managed to do it without a metaclass:
class MyClass:
def __init__(self):
self.param1 = 7
self.param2 = 8
def __setattr__(self, key, value):
if key not in self.__dict__ or len(self.__dict__.keys()) < 2:
self.__dict__[key] = value
raise ValueError("Excess of parameters.")
test = MyClass()
test.param1 = 3
test.param2 = 5
print(test.param1) # Prints 3
print(test.param2) # Prints 5
test.param3 = 9 # RAISES AN ERROR, AS IT SHOULD!
The problem that I am having is that I want to do this through a metaclass. Sure, I bet that there can be better options, but I find that, in this case, the best option is to do it through a metaclass (?)
I have read several posts and this is the closest Q&A I have found to solving the problem.
Is there a way to limit the number of attributes of a class to a specific number, at creation time, using metaclasses?
Maybe use the dunder method __settattr__
to use the super class constructor?
If you really want to do it using metaclasses, you can, but I would recommend against it, because a class decorator suffices, and you can chain class decorators in future, whereas you cannot do so with metaclasses. Using a class decorator also frees up the possibility of using a metaclass in future.
With that in mind, here are the solutions I've cooked up. All code is tested and confirmed to function as intended on Python 3.7.3.
# A solution using a class decorator (recommended solution)
def limited_attributes(number_attributes):
def __setattr__(self,key,value):
if key not in self.__dict__ and len(self.__dict__.keys())>=number_attributes:
raise ValueError('Too many attributes')
self.__dict__[key]=value
def decorator(cls):
cls.__setattr__=__setattr__
return cls
return decorator
@limited_attributes(2)
class Foo:
def __init__(self):
self.param1=4
self.param2=5
test = Foo()
test.param1=3 # no error
test.param2='bar' # no error
test.param3='baz' # raises ValueError
# A solution using a function as a metaclass
def limited_attributes(number_attributes):
def __setattr__(self,key,value):
if key not in self.__dict__ and len(self.__dict__.keys())>=number_attributes:
raise ValueError('Too many attributes')
self.__dict__[key]=value
def metaclass(name,bases,attrs):
cls=type(name,bases,attrs)
cls.__setattr__=__setattr__
return cls
return metaclass
class Foo(metaclass=limited_attributes(2)):
def __init__(self):
self.param1=4
self.param2=5
test = Foo()
test.param1=3 # no error
test.param2='bar' # no error
test.param3='baz' # raises ValueError