【问题标题】:Enum vs String as a parameter in a function枚举与字符串作为函数中的参数
【发布时间】:2015-01-08 13:57:15
【问题描述】:

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

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

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

class SIDE:
    RIGHT = 0
    LEFT = 1

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

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

【问题讨论】:

    标签: python numpy coding-style


    【解决方案1】:

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

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

    恕我直言,最重要的标准是用法:对于在模块甚至包中使用,字符串似乎很好,在公共 API 中我更喜欢枚举。

    【讨论】:

      【解决方案2】:

      [更新]

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

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

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

      class SIDE(object):
          RIGHT = 'right'
          LEFT = 'left'
      
      numpy.searchsorted(a, v, side=SIDE.RIGHT)
      

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

      [更新]

      Stefan 提出了一个公平的观点:

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

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

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

      【讨论】:

      • 恕我直言,这不是品味问题。遇到运行时错误是愚蠢的,如果它们以前可以非常简单地防止的话。我真的无法掌握许多/所有著名框架中的用法。
      • 这是一个旧答案。对于 Python 3.7+ 中的数据类和可选类型注释,讨论毫无意义。
      • 抱歉,但对我来说,“Python 引入数据类 - 结合可选类型注释和静态类型分析器(如 mypy)”回答问题或解决问题的方式并不明显问题。请给个提示好吗?
      【解决方案3】:

      我知道这个问题已经得到解答,但有一点根本没有解决:当使用 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 不会有问题。

      【讨论】:

        【解决方案4】:

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

        side=1, opt_type=0, order_type=6
        

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

        我也喜欢值是字符串的“枚举”:

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

        【讨论】:

          【解决方案5】:

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

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

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

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

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

          numpy 是较旧的 3rd 方软件包之一(至少它的根源像 numeric 是)。字符串值比枚举更常见。事实上,我想不出任何枚举(正如你定义的那样)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-06-07
            相关资源
            最近更新 更多