There is a way to make it work without partial, FunctionProxy or anything like that, but you have to go deep into metaclasses and the implementation of Enum. Building on the other answers, this works:
from enum import Enum, EnumType, _EnumDict, member
import inspect
class _ExtendedEnumType(EnumType):
# Autowraps class-level functions/lambdas in enum with member, so they behave as one would expect
# I.e. be a member with name and value instead of becoming a method
# This is a hack, going deep into the internals of the enum class
# and performing an open-heart surgery on it...
def __new__(metacls, cls: str, bases, classdict: _EnumDict, *, boundary=None, _simple=False, **kwds):
non_members = set(classdict).difference(classdict._member_names)
for k in non_members:
if not k.startswith("_"):
if classdict[k].__class__ in [classmethod, staticmethod]:
continue
# instance methods don't make sense for enums, and may break callable enums
if "self" in inspect.signature(classdict[k]).parameters:
raise TypeError(
f"Instance methods are not allowed in enums but found method"
f" {classdict[k]} in {cls}"
)
# After all the input validation, we can finally wrap the function
# For python<3.11, one should use `functools.partial` instead of `member`
callable_val = member(classdict[k])
# We have to use del since _EnumDict doesn't allow overwriting
del classdict[k]
classdict[k] = callable_val
classdict._member_names[k] = None
return super().__new__(metacls, cls, bases, classdict, boundary=boundary, _simple=_simple, **kwds)
class ExtendedEnum(Enum, metaclass=_ExtendedEnumType):
pass
Now you can do:
class A(ExtendedEnum):
a = 3
b = lambda: 4
@classmethod
def afunc(cls):
return 5
@staticmethod
def bfunc():
pass
Everything will work as expected.
PS: For some more Enum magic, I also like to add
def __getitem__(cls, item):
if hasattr(item, "name"):
item = item.name
# can't use [] because of particularities of super()
return super().__getitem__(item)
to _ExtendedEnumType, so that A[A.a] works.
See also this thread.
`