Algorithm to park cars with minimal moves (2nd version)
Why do you need a separate position dict, that you have to maintain whenever you move a car ? You can just use input.index(car_id) anytime you need to get a car position.
Also your print_move() function is almost useless, since it just swaps 2 positions, one of which is assumed to be empty. That could be written in a single line. 

Here are my proposed improvements, with an modified move() that takes the car id to move and the target free slot, and returns the new freed slot:

def car_move_v2(initial_slots, target_slots, EMPTY=None):
    current_slots = initial_slots # will use as working slots
    def move(car, free_slot):
        "move car to free_slot, returns the newly freed slot"
        assert current_slots[free_slot] == EMPTY, "Slot {0} is not free".format(free_slot)
        car_slot = current_slots.index(car)
        current_slots[free_slot], current_slots[car_slot] = current_slots[car_slot], EMPTY
        print('car {0} moved from {1} to {2}, {3}'.format(car, car_slot, free_slot, current_slots))
        return car_slot

    for slot, (current_car, target_car) in enumerate(zip(current_slots, target_slots)):
        if current_car == target_car: # if car is already in target position, do not move
            continue
        elif current_car != EMPTY: # if there is an unwanted car in the slot, move it to the empty slot
            free_slot = move(current_car, current_slots.index(EMPTY))
        else:
            free_slot = slot
        # at this point we have a free slot to work with
        while target_slots[free_slot] != EMPTY:
            other_target_car = target_slots[free_slot]
            free_slot = move(other_target_car, free_slot) # move that car to the current free pos


if __name__ == "__main__":
    car_move_v2(initial_slots=[None,1,2,3,7,6,4,5], target_slots=[4,6,5,1,7,3,2,None], EMPTY=None)