You can use __getattribute__
here, something to the effect of:
In [1]: class MyClass:
...: def __init__(self) -> None:
...: self.acting = False
...: self.att1 = None
...: self.att2 = None
...: self.att3 = None
...:
...: def __getattribute__(self, attribute):
...: if attribute.startswith("set_") and not self.acting:
...: raise RuntimeError(f"{self} is not acting")
...: return super().__getattribute__(attribute)
...:
...: def set_att1(self, value):
...: self.att1 = value
...:
...: def set_att2(self, value):
...: self.att2 = value
...:
...: def set_att3(self, value):
...: self.att3 = value
...:
...: def switch_acting(self):
...: self.acting = not self.acting
...:
In [2]: instance = MyClass()
In [3]: instance.set_att1(10)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-3-ead9d2b57142> in <module>
----> 1 instance.set_att1(10)
<ipython-input-1-585c5de84d26> in __getattribute__(self, attribute)
8 def __getattribute__(self, attribute):
9 if attribute.startswith("set_") and not self.acting:
---> 10 raise RuntimeError(f"{self} is not acting")
11 return super().__getattribute__(attribute)
12
RuntimeError: <__main__.MyClass object at 0x7f9a00520af0> is not acting
In [4]: instance.switch_acting()
In [5]: instance.set_att1(10)
In [6]: instance.att1
Out[6]: 10
However, as noted in the comments, this will check the self.acting
flag when the attribute is looked up, which is not necessarily when the method is called.
We can more carefully handle the fact that the verification should happen when the method is called, not accessed:
class MyClass:
def __init__(self) -> None:
self.acting = False
self.att1 = None
self.att2 = None
self.att3 = None
def __getattribute__(self, attribute):
if attribute.startswith("set_"):
bound_method = super().__getattribute__(attribute).__get__(self, type(self))
def _wrapper(*args, **kwargs):
if not self.acting:
raise RuntimeError(f"{self} is not acting")
return bound_method(*args, **kwargs)
return _wrapper
return super().__getattribute__(attribute)
def set_att1(self, value):
self.att1 = value
def set_att2(self, value):
self.att2 = value
def set_att3(self, value):
self.att3 = value
def switch_acting(self):
self.acting = not self.acting
A demonstration:
In [29]: instance = MyClass()
In [30]: instance.set_att1(10)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-30-ead9d2b57142> in <module>
----> 1 instance.set_att1(10)
<ipython-input-28-53d1a32f1498> in _wrapper(*args, **kwargs)
11 def _wrapper(*args, **kwargs):
12 if not self.acting:
---> 13 raise RuntimeError(f"{self} is not acting")
14 return bound_method(*args, **kwargs)
15 return _wrapper
RuntimeError: <__main__.MyClass object at 0x7f99e05bf340> is not acting
In [31]: instance.switch_acting()
In [32]: instance.set_att1(10)
In [33]: instance.att1
Out[33]: 10
In [34]: f = instance.set_att2
In [35]: instance.switch_acting()
In [36]: f(20)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-36-a5c3c0536773> in <module>
----> 1 f(20)
<ipython-input-28-53d1a32f1498> in _wrapper(*args, **kwargs)
11 def _wrapper(*args, **kwargs):
12 if not self.acting:
---> 13 raise RuntimeError(f"{self} is not acting")
14 return bound_method(*args, **kwargs)
15 return _wrapper
RuntimeError: <__main__.MyClass object at 0x7f99e05bf340> is not acting
In [37]: instance.switch_acting()
In [38]: f(20)
In [39]: instance.att2
Out[39]: 20
EDIT: I think I did the opposite of what you wanted, but the same should work, just check if self.acting
instead of if not self.acting