I'd like to use named constants whereever possible instead of providing literal values or longish function signatures with a lot of boolean args.
Therefore i like pythons enum.Flag
or enum.Enum
.
More precisely, I would like to pass an argument to a function which holds a bit combination of enum.Flags. And i would like to avoid writing module.TheFlags.flagX
for every set flag I would like to pass to the function. The flags should replace the boolean args.
I came up with following code:
import enum
class AvailableFlags(enum.Flag):
flag1 = enum.auto()
flag2 = enum.auto()
class FuncFlags:
def __init__(self):
self._flags = AvailableFlags(0)
@property
def flag1(self):
self._flags |= AvailableFlags.flag1
return self
@property
def flag2(self):
self._flags |= AvailableFlags.flag2
return self
def __str__(self):
return str(self._flags.value)
def func(setup_flags: FuncFlags):
print(setup_flags)
if __name__ == "__main__":
func(FuncFlags().flag1)
func(FuncFlags().flag2)
func(FuncFlags().flag1.flag2)
func(FuncFlags())
It creates instances of FuncFlags and then mis-uses the properties to set single flags returning the changed object itself. However, one would expect that the property does NOT change object state. Therefore, this is obviously not a clean solution despite that it works, though.
So, my question is, how this can be implemented in a clean, reusable way?
Meanwhile, I found an answer by adding another level of indirection. I want to share it here if it is of interest for someone else. Object state is maintained as every invokation of a flag creates a new instance from the current instance by setting an additional flag. If we attempt to access an undefined flag an exception is raised (not shown).
import enum
class AvailableFlags(enum.Flag):
flag1 = enum.auto()
flag2 = enum.auto()
class FlagHelper:
def __init__(self, cls, value = 0):
self._cls = cls
self._flags = self._cls(value)
def __getattr__(self, item):
if item in self._cls.__members__:
return self.__class__(self._flags | getattr(self._cls, item))
getattr(self._cls, item) # Let attribute error pass through
def __str__(self):
return str(self._flags.value)
class FuncFlags(FlagHelper):
def __init__(self, value = 0):
super().__init__(AvailableFlags, value)
def func(setup_flags: FuncFlags):
print(setup_flags)
if __name__ == "__main__":
ff = FuncFlags()
func(ff.flag1)
func(ff.flag2)
func(ff.flag1.flag2)
func(ff)
Output:
1
2
3
0