<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def voxelize(obj, **kwargs):
 """ Generates binary voxel representation of the surfaces and volumes. Keyword Arguments: * ``grid_size``: size of the voxel grid. *Default: (8, 8, 8)* * ``padding``: voxel padding for in-outs finding. *Default: 10e-8* * ``use_cubes``: use cube voxels instead of cuboid ones. *Default: False* * ``num_procs``: number of concurrent processes for voxelization. *Default: 1* :param obj: input surface(s) or volume(s) :type obj: abstract.Surface or abstract.Volume :return: voxel grid and filled information :rtype: tuple """

    # Get keyword arguments
    grid_size = kwargs.pop('grid_size', (8, 8, 8))
    use_cubes = kwargs.pop('use_cubes', False)
    num_procs = kwargs.get('num_procs', 1)

    if not isinstance(grid_size, (list, tuple)):
        raise TypeError("Grid size must be a list or a tuple of integers")

    # Initialize result arrays
    grid = []
    filled = []

    # Should also work with multi surfaces and volumes
    for o in obj:
        # Generate voxel grid
        grid_temp = vxl.generate_voxel_grid(o.bbox, grid_size, use_cubes=use_cubes)
        args = [grid_temp, o.evalpts]

        # Find in-outs
        filled_temp = vxl.find_inouts_mp(*args, **kwargs) if num_procs > 1 else vxl.find_inouts_st(*args, **kwargs)

        # Add to result arrays
        grid += grid_temp
        filled += filled_temp

    # Return result arrays
    return grid, filled