4 distinct integers, whose reciprocals sum up to 1
Fraction has numerator and denominator, which are cleaner than the mucking around with int().

Appending to an external accumulator is quick and easy, but yield may give more readable code. If you combine that with passing the minimum next denominator and the remaining fraction (rather than respectively extracting them and recalculating them on each call) you get

from fractions import Fraction

def solve(num_terms, min_denom, sum_needed):
  if num_terms == 1:
    if sum_needed.numerator == 1 and sum_needed.denominator &gt;= min_denom:
      yield [sum_needed.denominator]
  else:
    next_min = max(min_denom, int(Fraction(1,sum_needed)) + 1)
    next_max = int(Fraction(num_terms,sum_needed))
    for next in range(next_min, next_max+1):
      yield from [[next] + tail for tail in
                  solve(num_terms - 1, next + 1, sum_needed - Fraction(1, next))]

print (list(solve(4, 1, Fraction(1,1))))


Obviously readability is subjective to some degree, but I prefer the version with yield.

I should note that it would be more Pythonic to use a double comprehension instead of a comprehension inside a loop, but I don't find double comprehensions very readable.



itertools can be used, but I don't think there's any way to do so really efficiently - i.e. with early aborts. I think you would have to bound the maximum possible denominator:

# The largest denominator will be the last term when the previous terms are
# as large as possible
recip_max_denom = 1 - sum([Fraction(1,x) for x in range(1, num_terms)]
max_denom = recip_max_denom.denominator // recip_max_denom.numerator


Then you can use

itertools.combinations(range(1, max_denom + 1), num_terms)


and filter to those which give the correct sum.