Is there a smart way to apply a better transition between colours while maintaining one solid colour per face?
The following script reads the face position normalize it and assign it to the vertex color of this face ( new group named 'face_pos' will be created ) :

import bpy

obj = bpy.context.object
obj_data = obj.data
group_name = 'face_pos'
#check for existing group with the same name
if None == obj_data.vertex_colors.get(group_name): 
    obj_data.vertex_colors.new(name=group_name)
color_map =  obj_data.vertex_colors[group_name]

#collect dimension info
dim = obj.dimensions
min_x = -1*min([p.center[0] for p in obj_data.polygons])
min_y = -1*min([p.center[1] for p in obj_data.polygons])
min_z = -1*min([p.center[2] for p in obj_data.polygons])

for poly in obj_data.polygons:
    pos = poly.center
    color = ((pos[0]+min_x)/dim[0], (pos[1]+min_y)/dim[1], (pos[2]+min_z)/dim[2])
    for loop_ind in poly.loop_indices:
        color_map.data[loop_ind].color = color 


we wont apply this vertex color but instead we use it as a face position input in the material nodes using the Attribute node and the vertex color name , then we manipulate this information to create the desired effect 

material node 

the result is color transition for faces not points :





This solution maybe a bit complicated but it introduce a trick for accessing other properties in cycles(this is not like drivers since it updates per point not frame)