Muppet
Muppet

Reputation: 6019

Enum vs String as a parameter in a function

I noticed that many libraries nowadays seem to prefer the use of strings over enum-type variables for parameters.

Where people would previously use enums, e.g. dateutil.rrule.FR for a Friday, it seems that this has shifted towards using string (e.g. 'FRI').

Same in numpy (or pandas for that matter), where searchsorted for example uses of strings (e.g. side='left', or side='right') rather than a defined enum. For the avoidance of doubt, before python 3.4 this could have been easily implemented as an enum as such:

class SIDE:
    RIGHT = 0
    LEFT = 1

And the advantages of enums-type variable are clear: You can't misspell them without raising an error, they offer proper support for IDEs, etc.

So why use strings at all, instead of sticking to enum types? Doesn't this make the programs much more prone to user errors? It's not like enums create an overhead - if anything they should be slightly more efficient. So when and why did this paradigm shift happen?

Upvotes: 35

Views: 21326

Answers (5)

dddJewelsbbb
dddJewelsbbb

Reputation: 646

I understand this question has already been answered, but there is one thing that has not at all been addressed: the fact that Python Enum objects must be explicitly called for their value when using values stored by Enums.

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

One simple solution to this problem is to offer an implementation of __str__()

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

Yes, adding .value is not a huge deal, but it is an inconvenience nonetheless. Using regular strings requires zero extra effort, no extra classes, or redefinition of any default class methods. Still, there must be explicit casting to a string value in many cases, where a simple str would not have a problem.

Upvotes: 10

Paulo Scardine
Paulo Scardine

Reputation: 77241

[update]

As of today (2019) Python introduced dataclasses - combined with optional type annotations and static type analyzers like mypy I think this is a solved problem.

As for efficiency, attribute lookup is somewhat expensive in Python compared to most computer languages so I guess some libraries may still chose to avoid it for performance reasons.

[original answer]

IMHO it is a matter of taste. Some people like this style:

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

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

Yes, if you call searchsorted with side='foo' you may get an AssertionError way later at runtime - but at least the bug will be pretty easy to spot looking the traceback.

While other people may prefer (for the advantages you highlighted):

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

I favor the first because I think seldom used constants are not worth the namespace cruft. You may disagree, and people may align with either side due to other concerns.

If you really care, nothing prevents you from defining your own "enums":

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

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

I think it is not worth but again it is a matter of taste.

[update]

Stefan made a fair point:

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 :-)

I can see how painful this can be in a language without named parameters - using the example you have to search for the string 'right' and get a lot of false positives. In Python you can narrow it down searching for side='right'.

Of course if you are dealing with an interface that already has a defined set of enums/constants (like an external C library) then yes, by all means mimic the existing conventions.

Upvotes: 8

hpaulj
hpaulj

Reputation: 231325

Strictly speaking Python does not have enums - or at least it didn't prior to v3.4

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

I prefer to think of your example as programmer defined constants.

In argparse, one set of constants have string values. While the code uses the constant names, users more often use the strings.

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

numpy is one of the older 3rd party packages (at least its roots like numeric are). String values are more common than enums. In fact I can't off hand think of any enums (as you define them).

Upvotes: 1

acushner
acushner

Reputation: 9946

i prefer strings for the reason of debugging. compare an object like

side=1, opt_type=0, order_type=6

to

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

i also like "enums" where the values are strings:

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

Upvotes: 3

Stefan Braun
Stefan Braun

Reputation: 176

I think enums are safer especially for larger systems with multiple developers.

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 :-)

The most important criteria IMHO is the usage: for use in a module or even a package a string seems to be fine, in a public API I'ld prefer enums.

Upvotes: 14

Related Questions