3

I'm trying to find a way to exec() an if statement.

So, for example:

exampleCode = "if 0 < 1:"

exec(exampleCode)
    print("Zero is less than one")

Now, obviously this isn't possible with exec(). But is it with something similar?

Something else that would work would be:

exampleCode = "if 0 < 1:"

exampleCode
    print("Zero is less than one")

Again, this isn't possible, because variables cannot be code.

So, again, is there something else that may work?

Quelklef
  • 1,999
  • 2
  • 22
  • 37
  • Just use a lambda. You're aware that exec/eval are considered harmful and there are other ways to do this? – smci Jun 16 '15 at 02:29
  • As I understand it, they're harmful due to possible exploitation of user input, but in the actual code I'm using it in, the user input is not what is used. Instead, a good example would be that they choose from a list of what code they want to be used, getting rid of this risk. – Quelklef Jun 16 '15 at 02:31
  • PieCrust: perhaps, but there are much less tortured ways of doing that. That's just a switch statement (Pythonic idiom: use a dict whose values return expressions) or if/elif/else ladder. – smci Jun 16 '15 at 02:33
  • Here is **[How to implement a switch statement using a dict in Python](http://stackoverflow.com/questions/374239/why-doesnt-python-have-a-switch-statement/374276)** – smci Jun 16 '15 at 02:49
  • Near-duplicate of [**What's the difference between eval, exec, and compile in Python?**](https://stackoverflow.com/questions/2220699/whats-the-difference-between-eval-exec-and-compile-in-python), which already has good complete answers. – smci Jan 11 '18 at 18:56
  • @smci I would say not duplicate because the intent of this question seems to be about, in essence, using templates in python. – Quelklef Jan 12 '18 at 13:04

4 Answers4

7

You can do this, sort of, with eval:

exampleCode = "0 < 1"
if eval(exampleCode):
    print("Zero is less than one")

...but it's unclear what the benefit is of doing this (that is, there's something you're not showing us that motivates your question).

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • Doesn't `eval()` return the result of code? How would this work? – Quelklef Jun 16 '15 at 02:24
  • The `eval()` function returns the result of parsing and evaluating the given expression represented as a string. It works because that's what `eval()` does. – Greg Hewgill Jun 16 '15 at 02:26
  • Greg, keeping @PieCrust's `exampleCode` as it is, I think that it is better to remove the `if` and the `:` (which reduces to your `exampleCode` variable) and then continue your method, which is, using `eval`. (Just an idea) – Bhargav Rao Jun 16 '15 at 02:28
  • 1
    Exactly. And `0 < 1` isn't a function. – Quelklef Jun 16 '15 at 02:28
  • 1
    The string `"0 < 1"` is not a function, it's an *expression*, and that is what `eval()` evaluates. – Greg Hewgill Jun 16 '15 at 02:37
  • Sorry for accepting this answer two years late, I guess I forgot about it..? Anyway, I don't remember why I asked, but I can guess, and I think this response best addresses my question and the intent behind it. Thanks. – Quelklef Dec 12 '17 at 19:14
3

Use a lambda (an anonymous function). We can pass an arbitrary lambda which allows dynamically evaluating a condition in the scope in which it is evaluated:

# 1) A lambda with 0 args; this is silly because it will always evluate to a constant
cond_0args = lambda: 0 < 1
# <function <lambda> at 0x1084ffc80>

# 2) A lambda with 2 args:
cond = lambda x, y: x<y

# ... you could of course have a lambda with 3 args or whatever
#cond = lambda x, y, z: some_expression_involving_x_y_z

if cond_0args():
    print("Zero is less than one")

or, more sensibly:

if cond(0,1):
    print("Zero is less than one")
smci
  • 32,567
  • 20
  • 113
  • 146
  • See, this seems to work, but why, since _`0 < 1` isn't a full function?_ – Quelklef Jun 16 '15 at 02:35
  • PieCrust: yes it is, **Python lambda functions don't use `return`, they're just an expression**. @MotokoKusanagi: yes it is an answer. We pass an arbitrary lambda which allows dynamically evaluating a condition in the scope in which it is evaluated. – smci Jun 16 '15 at 02:38
  • @MotokoKusanagi it is a solution to my problem, and if it is not an answer to my question, then I phrased my question wrong. – Quelklef Jun 16 '15 at 02:40
  • PieCrust: don't appear to agree with him. He's wrong, and it is an answer to the question. Flagged his comment as unconstructive, which it is. (If he said he didn't understand why it was an answer, and asked that, that would be different...) – smci Jun 16 '15 at 02:41
  • But they `return`, still, even without the declaration, no? – Quelklef Jun 16 '15 at 02:42
  • PieCrust: don't get hung up on *"it can't be a proper function if it doesn't have a return keyword"*. All a function is, is a language construct for computing an expression. All a lambda is, is an unnamed function. Python lambdas don't use a return statement. No big deal. – smci Jun 16 '15 at 02:44
  • Since I don't have enough reputation to move this to chat, can you? – Quelklef Jun 16 '15 at 02:48
  • It wouldn't let me automatically either: "Pie Crust has only 18 reputation, not yet enough to chat". I had to manually create a **chat here: http://chat.stackoverflow.com/rooms/info/80619/discussion-about-python-lambdas-eval-exec-and-switch-statements?tab=general** – smci Jun 16 '15 at 02:54
  • I can't actually use chats we'll just have to continue here. – Quelklef Jun 16 '15 at 02:56
  • I think I'm thinking about lambdas wrong. In my mind, they are functions without names: You put in a value, and one comes out. So, since `0 < 1` takes no value, and has no output, how is it a valid lambda? – Quelklef Jun 16 '15 at 02:57
  • @Pie Crust: It does produce output. It returns `true`, since 0 is less than 1. And in the same way that `def fn():` doesn't take any arguments, neither does that lambda. Note that you can also create lambdas that do take arguments: `lambda z: z < 1` – recursive Jun 16 '15 at 03:02
  • I've actually got to go. I'll try to research more & understand, but if I don't, I'll be posting in these comments (if that's ok with you) – Quelklef Jun 16 '15 at 03:02
  • Aha! I got it now! So they just _calculate_ an expression and then return the result. Which means the code we were using just said `if true, print("blah")`! Thank you so much, I understand now, _and_ I think I'll be able to use this in my program. Cheers! – Quelklef Jun 16 '15 at 03:54
  • Right, just the evaluation of the expression is deferred. – smci Jun 16 '15 at 04:41
  • @smci Hello! I am working on almost exactly the same problem, and my question is: If we are given a string of `exampleCode = "0 < 1"` how can we construct the `lambda` using the `exampleCode ` but not the `0 < 1` directly. Defining `cond = lambda: exampleCode ` and then calling `if cond(): print("Zero is less than one")` doesn't work. – FatihAkici Jan 09 '18 at 07:05
  • @FatihAkici: `"0 < 1"` is not an expression, but a string containing an expression, so your lambda would need to `eval(cond_str)`. Anyway turns out this is a near-duplicate of [**What's the difference between eval, exec, and compile in Python?**](https://stackoverflow.com/questions/2220699/whats-the-difference-between-eval-exec-and-compile-in-python) – smci Jan 11 '18 at 18:53
  • Lambdas are anonymous functions. Like regular functions, they defer evaluating their args until they're evaluated, and in the scope in which they are evaluated. `0 < 1` totally has a value, just as much as `2 + 3` ! That's all there is to it... read more about lambdas in doc or here on SO... – smci Feb 11 '21 at 22:50
0

Python has to know the syntactical role of every line of code when it compiles a block. Allowing things like what you're trying to do causes a lot of complications, because there could be things like

line = random.choice(['if flag:', '    print(1)'])

flag = True
for i in range(5):
    print(1)
exec(random.choice(['if flag:', '    print(1)'])) # What indentation level is this?
    print(2) # Is this part of the loop?

If you want to do what you're trying to do, you'll have to make the whole block a string and exec it, with the part that changes from run to run substituted in:

template = '''\
{statement}
    print("Zero is less than one")'''

exec(statement.format('if 0 < 1:'))

Using exec at all is generally a bad idea, though.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Again, since the users won't be able to directly input code to be `exec()`uted, I see no risk. – Quelklef Jun 16 '15 at 02:37
  • Unfortunately, I'm not familiar enough with Python to understand your code, so I'll leave it, since other solutions seem to work. – Quelklef Jun 16 '15 at 02:38
  • @PieCrust: This answer isn't about risk; it's about how you can't really define exec in such a way that exec-ing a control statement makes sense. (The part at the end is about risk a little, but it's more that if you're using `exec`, you're usually thinking in terms of "what code would I type for each case" instead of more useful ways to think about your problems.) – user2357112 Jun 16 '15 at 02:44
0

If you are making a sort of quiz program with questions like "What piece of code goes here" (at least that is what I got from your comments) than a dictionary would work fine:

answers = {1: (True, 'if 0 < 1:') 2: (False, '...'}
answer = input('what code goes here?')
for k, v in answers:
    print('{} - {}'.format(k, v[1])

if answers[answer][0]:
    print('correct')
else:
    print('incorrect')
kylieCatt
  • 10,672
  • 5
  • 43
  • 51
  • This is just a switch statement implemented in Python as a dict, as I already suggested above in comments. – smci Jun 16 '15 at 02:46
  • No, that was just an example to explain easily without having to talk about my entire program when this is jsut a snippet. – Quelklef Jun 16 '15 at 02:47
  • Quelklef I was talking about @IanAuld's answer, not your question. – smci Jan 11 '18 at 19:15