How to define enum values that are functions?

你离开我真会死。 提交于 2020-06-22 13:31:09

问题


I have a situation where I need to enforce and give the user the option of one of a number of select functions, to be passed in as an argument to another function:

I really want to achieve something like the following:

from enum import Enum

#Trivial Function 1
def functionA():
    pass

#Trivial Function 2
def functionB():
    pass

#This is not allowed (as far as i can tell the values should be integers)
#But pseudocode for what I am after
class AvailableFunctions(Enum):
    OptionA = functionA
    OptionB = functionB

So the following can be executed:

def myUserFunction(theFunction = AvailableFunctions.OptionA):
   #Type Check
   assert isinstance(theFunction,AvailableFunctions) 

   #Execute the actual function held as value in the enum or equivalent
   return theFunction.value() 

回答1:


Your assumption is wrong. Values can be arbitrary, they are not limited to integers. From the documentation:

The examples above use integers for enumeration values. Using integers is short and handy (and provided by default by the Functional API), but not strictly enforced. In the vast majority of use-cases, one doesn’t care what the actual value of an enumeration is. But if the value is important, enumerations can have arbitrary values.

However the issue with functions is that they are considered to be method definitions instead of attributes!

In [1]: from enum import Enum

In [2]: def f(self, *args):
   ...:     pass
   ...: 

In [3]: class MyEnum(Enum):
   ...:     a = f
   ...:     def b(self, *args):
   ...:         print(self, args)
   ...:         

In [4]: list(MyEnum)  # it has no values
Out[4]: []

In [5]: MyEnum.a
Out[5]: <function __main__.f>

In [6]: MyEnum.b
Out[6]: <function __main__.MyEnum.b>

You can work around this by using a wrapper class or just functools.partial or (only in Python2) staticmethod:

from functools import partial

class MyEnum(Enum):
    OptionA = partial(functionA)
    OptionB = staticmethod(functionB)

Sample run:

In [7]: from functools import partial

In [8]: class MyEnum2(Enum):
   ...:     a = partial(f)
   ...:     def b(self, *args):
   ...:         print(self, args)
   ...:         

In [9]: list(MyEnum2)
Out[9]: [<MyEnum2.a: functools.partial(<function f at 0x7f4130f9aae8>)>]

In [10]: MyEnum2.a
Out[10]: <MyEnum2.a: functools.partial(<function f at 0x7f4130f9aae8>)>

Or using a wrapper class:

In [13]: class Wrapper:
    ...:     def __init__(self, f):
    ...:         self.f = f
    ...:     def __call__(self, *args, **kwargs):
    ...:         return self.f(*args, **kwargs)
    ...:     

In [14]: class MyEnum3(Enum):
    ...:     a = Wrapper(f)
    ...:     

In [15]: list(MyEnum3)
Out[15]: [<MyEnum3.a: <__main__.Wrapper object at 0x7f413075b358>>]

Also note that if you want you can define the __call__ method in your enumeration class to make the values callable:

In [1]: from enum import Enum

In [2]: def f(*args):
   ...:     print(args)
   ...:     

In [3]: class MyEnum(Enum):
   ...:     a = partial(f)
   ...:     def __call__(self, *args):
   ...:         self.value(*args)
   ...:         

In [5]: MyEnum.a(1,2,3)   # no need for MyEnum.a.value(1,2,3)
(1, 2, 3)



回答2:


In addition to the answer of Bakuriu... If you use the wrapper approach like above you loose information about the original function like __name__, __repr__ and so on after wrapping it. This will cause problems for example if you want to use sphinx for generation of source code documentation. Therefore add the following to your wrapper class.

class wrapper:
    def __init__(self, function):
        self.function = function
        functools.update_wrapper(self, function)
    def __call__(self,*args, **kwargs):
        return self.function(*args, **kwargs)
    def __repr__(self):
        return self.function.__repr__()


来源:https://stackoverflow.com/questions/40338652/how-to-define-enum-values-that-are-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!