Enum vs String как параметр в функции

Я заметил, что многие библиотеки в настоящее время, похоже, предпочитают использование строк над переменными типа перечисления для параметров.

Если люди ранее использовали перечисления, например. dateutil.rrule.FR в пятницу, похоже, что это смещено в сторону использования строки (например, "FRI" ).

То же самое в numpy (или pandas, если на то пошло), где searchsorted, например, использует строки (например, side = 'left' или side = 'right') вместо определенного перечисления. Во избежание сомнений, перед python 3.4 это могло быть легко реализовано как перечисление как таковое:

class SIDE:
    RIGHT = 0
    LEFT = 1

И преимущества переменной типа перечисления ясны: вы не можете пропустить их, не вызывая ошибки, они предлагают правильную поддержку для IDE и т.д.

Так зачем вообще использовать строки, а не придерживаться типов перечислений? Разве это не делает программы более подверженными ошибкам пользователя? Это не похоже на перечисления, создающие накладные расходы - если что-то они должны быть немного более эффективными. Итак, когда и почему произошел сдвиг этой парадигмы?

Ответ 1

[Обновление]

На сегодняшний день (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

Я думаю, что перечисления более безопасны, особенно для более крупных систем с несколькими разработчиками.

Как только возникает необходимость изменить значение такого перечисления, поиск и замена строки во многих местах - это не моя идея весело: -)

Наиболее важные критерии IMHO - это использование: для использования в модуле или даже пакете строка кажется прекрасной, в публичном API я предпочитаю перечисления.

Ответ 3

Я предпочитаю строки для отладки. сравните объект, например

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'

Ответ 4

Строго говоря, у Python нет перечислений - или, по крайней мере, это было не до v3.4

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

Я предпочитаю думать о вашем примере как о программируемых константах.

В argparse один набор констант имеет строковые значения. Хотя код использует имена констант, пользователи чаще используют строки.

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

numpy является одним из старых пакетов сторонних производителей (по крайней мере, его корни, такие как numeric). Значения строк более распространены, чем перечисления. На самом деле я не могу отвлечься от каких-либо перечислений (как вы их определяете).

Ответ 5

Я понимаю, что на этот вопрос уже был дан ответ, но есть одна вещь, которая вообще не была рассмотрена: тот факт, что объекты Python Enum должны явно вызываться для их значения при использовании значений, хранящихся в Enums.

>>> 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 не будет иметь проблем.