Enum vs String作为函数中的参数

时间:2015-01-08 13:57:15

标签: python numpy coding-style

我注意到现在很多库似乎更喜欢使用字符串而不是enum-type变量来表示参数。

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

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

class SIDE:
    RIGHT = 0
    LEFT = 1

enums-type变量的优点很明显:你不能在不引发错误的情况下拼错它们,它们为IDE提供了适当的支持等。

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

5 个答案:

答案 0 :(得分:5)

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

一旦需要改变这种枚举的价值,在许多地方查找和替换字符串并不是我的乐趣: - )

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

答案 1 :(得分:3)

[更新]

截至今日(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')

是的,如果您使用searchsorted致电side='foo',您可能会在运行时获得AssertionError方式 - 但至少可以很容易地看到该错误。

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

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

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

如果你真的在乎,没有什么能阻止你定义你自己的“枚举”:

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

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

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

[更新]

斯蒂芬提出了一个公平的观点:

  

一旦需要改变这种枚举的价值,在许多地方查找和替换字符串并不是我的乐趣: - )

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

当然,如果您正在处理已经有一组已定义的枚举/常量(如外部C库)的接口,那么肯定是模仿现有的约定。

答案 2 :(得分:1)

我更喜欢调试字符串。比较像

这样的对象
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'

答案 3 :(得分:1)

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

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

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

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

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

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

答案 4 :(得分:1)

我知道这个问题已经得到回答,但是有一点根本没有解决:使用Enums存储的值时,必须显式调用Python Enum对象的值这一事实。

>>> 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并不是什么大问题,但这仍然很麻烦。使用常规字符串需要零额外的精力,无需额外的类或重新定义任何默认类方法。不过,在许多情况下,简单的str不会出现问题,必须显式转换为字符串值。