Constructing expressions that produce integers by only using digits in string '2017' and high school level operators and functions
Where your code is currently lacking is using all the knowledge you have. You already know that in the end the digits have to be in order and that in between the digits there can (or can not) be an operator.

This means, we only need to generate all strings of the form s = "{}2{}0{}1{}7{}", where the {} denote a place for an operator.

In addition, we know that certain operators will never make sense at the beginning (like /) and most will not make sense at the end either (basically, only ) makes sense there).

If we use this fact, we can end up with something like:

from collections import defaultdict
from itertools import product
from math import *

allowed_begin = set(["", "+", "-", "sin(", "cos(", "tan(", "exp(", "("])
allowed = allowed_begin | set(
    ["*", "/", "**", "*sin(", "*cos(", "*tan(", "*exp(", "*(", ")"])
allowed_end = set(["", ")"])

def find_expressions():
    expressions = defaultdict(list)
    template = "{}2{}0{}1{}7{}"
    for ops in product(allowed_begin, allowed, allowed, allowed, allowed_end):
        try:
            expression = template.format(*ops)
            n = eval(expression)
            if n &gt;= 0 and n % 1 == 0:
                print n, x
                expressions[n].append(expression)
        except Exception:
            pass

    return expressions

if __name__ == "__main__":
    expressions = find_expressions()
    print sorted(expressions.keys())


This runs in about 0.5s on my machine and finds expressions for
0.0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20.0, 21, 26, 27, 28, 30, 34, 37, 120, 140, 160, 194, 208, 340, 1407, 2017, 32768, 1.1719142372802612e+16, 13107200000000000000000L, 6.327431707155585e+60, 1.7911398206275708e+84, 2.1540324218248465e+90, 4.572185553551339e+147. They are stored in expressions and all possible expressions for an existing key can be accesses via e.g. expressions[22].

Note that I chose to allow *( as an operator, meaning implicit multiplication (see comment on the question). If you think this is not allowed, just take them out of the allowed operators.

The loop approach has one advantage over itertools.permutations. It allows a == b == c == d. Whereas permutations does not:

&gt;&gt;&gt; list(itertools.permutations([1,2], 2))
[(1,2), (2,1)]


Note the absence of (1,1) and (2,2).