Here is slow brute force way to find the largest palindromic product of three digit integers. You could do loop and break out of it if you go under the smaller number (the second one) of best solution found so far to be more efficient etc.

def pali(a):
    return str(a) == str(a)[::-1]

print('Biggest palindromic product of two three digit integers is %i, which is %i * %i.' % 
       max((a*b, a, b) for a in range(999, 100, -1) for b in range(a, 100000//a, -1) if pali(a*b)))
""" Output:
Biggest palindromic product of two three digit integers is 906609, which is 993 * 913.
"""

Well that just blew my mind.

Is it actually quicker to define the pali() function than it would be to just put that expression into the generator expression? EG

       max((a*b, a, b) for a in range(999, 100, -1) for b in range(a, 100000//a, -1) if str(a*b))==str(a*b)[::-1])

I ask because sometimes my list comprehensions get really crowded and I never think to do this.

It is faster by one function call penalty per iteration to not to use function, but function compensate it by making the code more readable. Your generator version is incorrect, you misplaced a ')':

max((a*b, a, b) for a in range(999, 100, -1) for b in range(a, 100000//a, -1) if str(a*b)==str(a*b)[::-1])

The optimized version with loop, even this above one liner is for me fast enough

def pali(a):
    return str(a) == str(a)[::-1]

m = (0,0,0)
for a in range(999, 100, -1):
    if m[-1] > a:
        #print('Breaking with a = %i' % a)
        break
    for b in range(a, 100000//a, -1):
        if pali(a*b):
            if a * b > m[0]:
                m = a*b, a, b
                #print m
            break


print('Biggest palindromic product of two three digit integers is %i, which is %i * %i.' % m)

""" Debug output:
(580085, 995, 583)
(906609, 993, 913)
Breaking with a = 912
Biggest palindromic product of two three digit integers is 906609, which is 993 * 913.
"""

Ya I would agree that the tradeoff for readability is worth it.

Thanks for sharing the snippet.