Is it possible to make a class which lets you stack enum Flags?

478 Views Asked by At

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?

2

There are 2 best solutions below

0
Wör Du Schnaffzig On BEST ANSWER

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
1
Ethan Furman On

I'm not really clear on what you are trying to accomplish, but perhaps this helps?

import enum

class AvailableFlags(enum.Flag):
    flag1 = enum.auto()
    flag2 = enum.auto()

flag1, flag2 = AvailableFlag


def func(setup_flags: AvailableFlags):
    print(setup_flags)


if __name__ == "__main__":
    func(flag1)
    func(flag2)
    func(flag1|flag2)
    func()