如何定义作为函数的枚举值?

Nic*_*ton 13 python enums function

我有一种情况需要强制执行,并为用户提供多个select函数之一的选项,作为参数传递给另一个函数:

我真的想要实现以下内容:

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
Run Code Online (Sandbox Code Playgroud)

所以可以执行以下操作:

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() 
Run Code Online (Sandbox Code Playgroud)

Bak*_*riu 25

你的假设是错误的.值可以是任意的,它们并不限于整数.从文档:

上面的示例使用整数来表示枚举值.使用整数简短而方便(并且由Functional API默认提供),但不严格执行.在绝大多数用例中,人们并不关心枚举的实际价值.但是,如果值很重要,枚举可以具有任意值.

然而,函数的问题是它们被认为是方法定义而不是属性!

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>
Run Code Online (Sandbox Code Playgroud)

你可以通过使用包装类或只是解决这个问题functools.partial:

from functools import partial

class MyEnum(Enum):
    OptionA = partial(functionA)
    OptionB = partial(functionB)
Run Code Online (Sandbox Code Playgroud)

样品运行:

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>)>
Run Code Online (Sandbox Code Playgroud)

或者使用包装类:

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>>]
Run Code Online (Sandbox Code Playgroud)

另请注意,如果需要,可以__call__在枚举类中定义方法以使值可调用:

In [1]: from enum import Enum

In [2]: from functools import partial

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

In [4]: 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)
Run Code Online (Sandbox Code Playgroud)

  • 不需要导入“partial”,顺便说一句,你可以只执行“a = staticmethod(f)”。 (2认同)

STe*_*kov 9

从 Python 3.11 开始,有更简洁、更容易理解的方式。membernonmember功能已添加到enum其他改进中,因此您现在可以执行以下操作:

from enum import Enum, member, nonmember

def fn(x):
    print(x)

class MyEnum(Enum):
    x = nonmember(1)
    meth = fn
    mem = member(fn)
    @classmethod
    def this_is_a_method(cls):
        print('No, still not a member')
    def this_is_just_function():
        print('No, not a member')
    @member
    def this_is_a_member(x):
        print('Now a member!', x)
Run Code Online (Sandbox Code Playgroud)

现在

>>> list(MyEnum)
[<MyEnum.mem: <function fn at ...>>, <MyEnum.this_is_a_member: <function MyEnum.this_is_a_member at ...>>]

>>> MyEnum.meth(1)
1

>>> MyEnum.mem(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'MyEnum' object is not callable

>>> MyEnum.mem.value(1)
1

>>> MyEnum.this_is_a_method()
No, still not a member

>>> MyEnum.this_is_just_function()
No, not a member

>>> MyEnum.this_is_a_member()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'MyEnum' object is not callable

>>> MyEnum.this_is_a_member.value(1)
Now a member! 1

>>> MyEnum.x
1
Run Code Online (Sandbox Code Playgroud)

当然,您可以添加__call__以避免显式.value调用:

class MyEnum(Enum):
    ...
    def __call__(self, *args, **kwargs):
        return self.value(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

现在

>>> MyEnum.mem(1)
1
Run Code Online (Sandbox Code Playgroud)

请注意,除非所有枚举成员函数共享相同的签名,否则无法正确键入此内容。


Abr*_*don 7

另一个不太笨重的解决方案是将函数放在元组中。正如 Bakuriu 提到的,您可能希望使枚举可调用。

from enum import Enum

def functionA():
    pass

def functionB():
    pass

class AvailableFunctions(Enum):
    OptionA = (functionA,)
    OptionB = (functionB,)

    def __call__(self, *args, **kwargs):
        self.value[0](*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样使用它:

AvailableFunctions.OptionA() # calls functionA
Run Code Online (Sandbox Code Playgroud)