枚举与字符串作为函数中的参数

Enum vs String as a parameter in a function

我注意到现在很多库似乎更喜欢使用字符串而不是枚举类型变量作为参数。

人们以前会在哪里使用枚举,例如dateutil.rrule.FR 一个星期五,似乎已经转向使用字符串(例如 'FRI')。

在 numpy(或 pandas 中相同),其中 searchsorted 例如使用字符串(例如 side='left',或 side='right') 而不是定义的枚举。为避免疑义,在 python 3.4 之前,这可以很容易地作为枚举实现:

class SIDE:
    RIGHT = 0
    LEFT = 1

而且枚举类型变量的优点很明显:你不能拼错它们而不会引发错误,它们为 IDE 等提供了适当的支持。

那么为什么要使用字符串而不是坚持使用枚举类型呢?这不会使程序更容易出现用户错误吗?这不像枚举会产生开销——如果有的话,它们应该稍微更有效率。那么,这种范式转变是何时以及为何发生的?

[更新]

截至今天(2019 年)Python 引入了数据类 - 结合可选的类型注释和静态类型分析器,如 mypy 我认为这是一个已解决的问题。

至于效率,与大多数计算机语言相比,属性查找在 Python 中有些昂贵,因此我猜出于性能原因,某些库可能仍会选择避免它。

[原答案]

恕我直言,这是一个品味问题。有人喜欢这种风格:

def searchsorted(a, v, side='left', sorter=None):
    ...
    assert side in ('left', 'right'), "Invalid side '{}'".format(side)
    ...

numpy.searchsorted(a, v, side='right')

是的,如果您使用 side='foo' 调用 searchsorted,您可能会在稍后的运行时获得 AssertionError 方式 - 但至少通过回溯可以很容易地发现错误。

虽然其他人可能更喜欢(因为您强调的优点):

numpy.searchsorted(a, v, side=numpy.CONSTANTS.SIDE.RIGHT)

我赞成第一个,因为我认为很少使用的常量不值得命名空间。你可能不同意,人们可能会因为其他问题而与任何一方保持一致。

如果您真的在意,没有什么能阻止您定义自己的 "enums":

class SIDE(object):
    RIGHT = 'right'
    LEFT = 'left'

numpy.searchsorted(a, v, side=SIDE.RIGHT)

我认为这不值得,但这又是一个品味问题。

[更新]

Stefan 说得很对:

As soon as the need arises to change the value of such an enum, looking up and replacing a string in many places is not my idea of fun :-)

我可以看到在没有命名参数的语言中这会有多痛苦 - 使用示例,您必须搜索字符串 'right' 并得到很多误报。在 Python 中,您可以缩小范围搜索 side='right'.

当然,如果您要处理的接口已经定义了一组 enums/constants(例如外部 C 库),那么是的,一定要模仿现有的约定。

我认为枚举更安全,尤其是对于具有多个开发人员的大型系统。

一旦需要更改此类枚举的值,在很多地方查找和替换字符串可不是我的乐趣:-)

恕我直言,最重要的标准是用法:在模块甚至包中使用字符串似乎没问题,在 public API 中我更喜欢枚举。

出于调试的原因,我更喜欢字符串。比较对象

side=1, opt_type=0, order_type=6

side='BUY', opt_type='PUT', order_type='FILL_OR_KILL'

我也喜欢 "enums" 其中值是字符串:

class Side(object):
    BUY = 'BUY'
    SELL = 'SELL'
    SHORT = 'SHORT'

严格来说 Python 没有枚举 - 或者至少在 v3.4 之前没有枚举

https://docs.python.org/3/library/enum.html

我更愿意将您的示例视为程序员定义的常量。

argparse中,一组常量具有字符串值。虽然代码使用常量名称,但用户更经常使用字符串。

 e.g. argparse.ZERO_OR_MORE = '*'
 arg.parse.OPTIONAL = '?'

numpy 是较旧的第 3 方软件包之一(至少它的根像 numeric 是)。字符串值比枚举更常见。事实上,我无法立即想到任何枚举(如您定义的那样)。

我知道这个问题已经得到解答,但有一件事根本没有得到解决:Python 使用枚举存储的值时,必须显式调用枚举对象以获取其值这一事实.

>>> class Test(Enum):
...     WORD='word'
...     ANOTHER='another'
...
>>> str(Test.WORD.value)
'word'
>>> str(Test.WORD)
'Test.WORD'

这个问题的一个简单解决方案是提供 __str__()

的实现
>>> class Test(Enum):
...     WORD='word'
...     ANOTHER='another'
...     def __str__(self):
...             return self.value
... 
>>> Test.WORD
<Test.WORD: 'word'>
>>> str(Test.WORD)
'word'

是的,添加 .value 不是什么大不了的事,但仍然带来不便。使用常规字符串需要零额外努力,不需要额外的 classes,也不需要重新定义任何默认的 class 方法。尽管如此,在许多情况下必须显式转换为字符串值,其中简单的 str 不会有问题。