Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 460
Muppet Avatar asked Jan 08 '15 13:01

Muppet


People also ask

Are enums better than strings?

I would consider Enums to be a better approach than Strings. They are type safe and comparing them is faster than comparing Strings. Show activity on this post. If your set of parameters is limited and known at compile time, use enum .

What is the difference between enum and string?

The obvious difference between numeric and string enums is that numeric enum values are mostly sequentially incremented automatically, while string enum values are not incremented; rather, each value is initialized independently.

Can enum be parameterized?

We have to create parameterized constructor for this enum class. Why? Because as we know that enum class's object can't be create explicitly so for initializing we use parameterized constructor. And the constructor cannot be the public or protected it must have private or default modifiers.

Can we use enum as string?

You can't. I think you have FOUR options here. All four offer a solution but with a slightly different approach... Option One: use the built-in name() on an enum.


5 Answers

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.

like image 149
Stefan Braun Avatar answered Oct 01 '22 21:10

Stefan Braun


[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.

like image 27
Paulo Scardine Avatar answered Oct 01 '22 19:10

Paulo Scardine


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.

like image 20
dddJewelsbbb Avatar answered Oct 01 '22 20:10

dddJewelsbbb


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'
like image 44
acushner Avatar answered Oct 01 '22 20:10

acushner


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

like image 29
hpaulj Avatar answered Oct 01 '22 20:10

hpaulj