Wrong shading as a result of "bisect_plane" in bmesh
Following the comments in the question and the edit of it, here is a script that automates the cuts and smooth normals to the original.

The principle is the following:


Copy the original mesh
Set a data transfer modifier on the copy targeting the original
Cut the copy
Remove double vertices (because the cuts may create doubles and these doubles explain what is shown in the edited question)
Apply the modifier


The code is commented below. But if the comments are not clear enough, please tell.

import bpy, bmesh
from math import pi
from mathutils import Vector

#Utility: test if obj exists and is a mesh
def MeshObject( obj ):
    return obj is not None and obj.type == 'MESH'

#Utility: go in the specified mode, call the lambda and returns to previous mode
def InMode( obj, mode, call ):
    prevMode = obj.mode
    result = None
    try:
        bpy.ops.object.mode_set( mode = mode, toggle = False )
        result = call()
    finally:   
        bpy.ops.object.mode_set( mode = prevMode, toggle = False )
    return result

#Cut the mesh and gets the normals for the vertices created during the cut
def CutMesh( obj, cuts ): #Original code from CoDEmanX

    bm = bmesh.from_edit_mesh( obj.data )

    for (plane_co, plane_no) in cuts:
        #Adds a bisectrice following the cut
        result = bmesh.ops.bisect_plane( bm, geom = bm.verts[:] + bm.edges[:] + bm.faces[:], plane_co = plane_co, plane_no = plane_no )
        #Gets the created bm edges from the result
        edges = [e for e in result['geom_cut'] if isinstance( e, bmesh.types.BMEdge )]
        #Splits the mesh according to these edges
        bmesh.ops.split_edges( bm, edges = edges )

    bmesh.update_edit_mesh( obj.data )

#Select all
def SelectAllVertices( obj ):
    for v in obj.data.vertices: v.select = True

#Get selected objects
def GetSelectedObjects():
    return bpy.context.selected_objects[:]

#Removes double vertices at the normals positions
def RemoveDoubles( obj ):
    bm = bmesh.from_edit_mesh( obj.data )
    bmesh.ops.remove_doubles( bm, verts = bm.verts[:] )
    bmesh.update_edit_mesh( obj.data )    

#Duplicate the object
def DuplicateObject( obj, scene ):
    newObj = obj.copy()
    newObj.data = obj.data.copy()
    newObj.animation_data_clear()
    scene.objects.link( newObj )
    return newObj

#Duplicate the mesh and add a data transfert modifier to the copy
def DuplicateObjectAndDataTransfert( obj ):
    copy = DuplicateObject( obj, bpy.context.scene )

    #Auto smooth is needed as if not, the normals will be lost when the modifier will be applied
    copy.data.use_auto_smooth = True

    #Create the modifier, keeping mainly the default options
    mod = copy.modifiers.new( "Data transfer", 'DATA_TRANSFER' )
    mod.object = obj
    mod.use_loop_data = True
    mod.data_types_loops = {'CUSTOM_NORMAL'}

    #The copy is now the selection
    obj.select = False        
    bpy.context.scene.objects.active = copy

    #Returns both copy and its modifier
    return copy, mod

#Creates cut objects from the selected object and normalize their normal to the original
def CutAndNormalize( cuts ):

    obj = bpy.context.object

    if MeshObject( obj ) and obj.mode == 'OBJECT':

        #Creates a base copy of the original object
        copy, mod = InMode( obj, 'OBJECT', lambda: DuplicateObjectAndDataTransfert( obj ) )

        #Cuts the copy according to the given cuts
        InMode( copy, 'EDIT', lambda: CutMesh( copy, cuts ) )
        InMode( copy, 'OBJECT', lambda: SelectAllVertices( copy ) )
        #Separates loose parts from the copy
        InMode( copy, 'EDIT', lambda: bpy.ops.mesh.separate( type = 'LOOSE' ) )

        #Get the objects created from the previous separation
        createdObjects = InMode( copy, 'OBJECT', lambda: GetSelectedObjects() )

        #And for each of them
        for createdObject in createdObjects:
            #Activate it
            bpy.context.scene.objects.active = createdObject
            #Select all...
            InMode( createdObject, 'OBJECT', lambda: SelectAllVertices( createdObject ) )
            #... in order to remove double vertices created by the cuts
            InMode( createdObject, 'EDIT', lambda: RemoveDoubles( createdObject ) )
            #And finally apply the data transfer modifier
            InMode( createdObject, 'OBJECT', lambda: bpy.ops.object.modifier_apply( modifier = mod.name ) )

print( '---------------' )

scale = 0.4
cuts = []
cuts.extend( [((i * scale, 0, 0), (1, 0, 0)) for i in range( -5, 5, 1 )] )
cuts.extend( [((0, i * scale, 0), (0, 1, 0)) for i in range( -5, 5, 1 )] )

CutAndNormalize( cuts )




Note1:

The result is not totally perfect where the cuts have created very small non quad polygons.

Note2:

I've tried to set the custom normals "manually" by affecting them directly. But I failed to do it. What happened is some per-face normals were wrong. I don't know why... if someone knows, please comment.