0

So I keep hearing everyone saying "don't use exec, don't use eval, etc." But I was wondering if it's correct to use under this cirumstance.

I have multiple functions that do the same thing for different operating systems:

def doCoolStuff_Windows():
    # do cool stuff here, windows edition

def doCoolStuff_Darwin():
    # do cool stuff here, mac edition

Then I have one master function that detects the user's operating system and runs the correct function for it.

I decided instead of doing a giant if-else thing, it would be easier to use exec like this:

def doCoolStuff():
    system = platform.system()
    exec "doCoolStuff_%s()" % system

Is this a correct usage of exec? Or is there a more correct way to accomplish the same task?

Thanks

TheInitializer
  • 566
  • 7
  • 20
  • Use an if statement instead of exec for that example – OneCricketeer Apr 29 '16 at 00:06
  • Briefly: no, it is not a good usage of `exec`. Use branches or a mapping. – TigerhawkT3 Apr 29 '16 at 00:07
  • exec is fine there, it's only a problem when you are dealing with unsanitized user input – Natecat Apr 29 '16 at 00:07
  • 3
    @Natecat - No, `exec` is not fine. It's totally unnecessary. There's no reason to introduce such a thing when a dictionary would suffice perfectly well. – TigerhawkT3 Apr 29 '16 at 00:08
  • @TigerhawkT3 this topic and the one you marked as duplicate have completely different questions, but the same answer. does that still make it a duplicate? If so that isn't really fair on my part as I didn't know that "variable variables" existed in another language, didn't know that's what they were called, and didn't know to look that up. – TheInitializer Apr 29 '16 at 20:13
  • 1
    Yes, it is still a duplicate. You had an [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). You aren't being unfairly penalized because you aren't being penalized at all. This question will now help others who are wondering whether they should use `exec` in this situation. :) – TigerhawkT3 Apr 29 '16 at 20:20
  • but people seem to be downvoting it because it's a duplicate :( – TheInitializer Apr 30 '16 at 14:58

1 Answers1

3

Not trying to start a flame war, but... No, imo that's not really a valid use of exec when there's a really easy alternative and very rarely is there ever a good reason to use eval.

You can simply setup a dispatch dict like so, or just use an if statement which would be simpler here.

 dispatch = {'Windows': doCoolStuff_Windows, 'Darwin': doCoolStuff_Darwin}
 def doCoolStuff():
     system = platform.system()
     if system in dispatch:
         dispatch[system]()
Pythonista
  • 11,377
  • 2
  • 31
  • 50
  • It is less readable though, I would actually argue that exec is a better solution here – Natecat Apr 29 '16 at 00:07
  • 3
    @Natecat - No, I would say that the dictionary solution is far superior. It could even be improved by doing away with the `if` and simply doing `dispatch.get(system, lambda: None)()`. – TigerhawkT3 Apr 29 '16 at 00:09
  • 2
    @Natecat I would think most people would prefer this version if for no other reason than it is 100% explicit about what operating systems are currently included in the list. Everything you need to know is right there. With the exec version, you have to go looking for it. – Rick Apr 29 '16 at 00:11
  • 2
    Note that big dispatch dicts can be made much more readable by splitting the entries into separate lines. – Rick Apr 29 '16 at 00:13
  • 1
    I'd never heard of a dispatch dict before, and it works really well. I'll mark this as the answer. Thanks guys! – TheInitializer Apr 29 '16 at 00:16
  • 1
    Pressing "dd" on my exec statement. Wow that felt good. – TheInitializer Apr 29 '16 at 00:17
  • 1
    @TigerhawkT3: Depending on the actual code it may be desirable to not even bother using `if` or `get`, since if `doCoolStuff` is called on an unsupported platform we probably _don't_ want it to fail silently. Instead, we can catch the `KeyError` (perhaps transmuting it into an `AssertionError`) and print a helpful error message. OTOH, it may be possible to provide a simple `doCoolStuff_default`, which can be supplied as the default arg to `get`. – PM 2Ring Apr 30 '16 at 09:02