Python program to calculate what pawns are safe
Playing with the code a bit, give insight in how we can optimize your code.

Unused computation

Note that the following part has no effect on the return value and can be removed:

    #delete the pawns which belong to the lowest row number
    for pawn in list_of_pawns:
        row_numbers.append(pawn[1])

    lowest_row_indexes = []

    for i, row_number in enumerate(row_numbers):
        if row_number == min(row_numbers):
            lowest_row_indexes.append(i)

    for i, lowest_row_index in enumerate(lowest_row_indexes):
        del list_of_pawns[lowest_row_index - i]
        del row_numbers[lowest_row_index - i]


Integer and String conversions

The lookup in the loop is expensive, as each time we convert strings to integers and vice versa. We make a mapping to integer once, prior to the loop.

def safe_pawns(pawns: set) -&gt; int:
    letters_string = "abcdefgh"
    safe_pawns_list = []

    pawns = list(map(lambda x: (letters_string.index(x[0]), int(x[1])), pawns))

    # check if there are neighbors which protect you
    for pawn in pawns:
        if pawn[0] == 0:
            if (letters_string[1], pawn[1] - 1) in pawns:
                safe_pawns_list.append(pawn)
        elif pawn[0] == 8:
            if (letters_string[6], pawn[1] - 1) in pawns:
                safe_pawns_list.append(pawn)
        else:
            if (pawn[0] + 1, pawn[1] - 1) in pawns:
                safe_pawns_list.append(pawn)
            elif (pawn[0] - 1, pawn[1] - 1) in pawns:
                safe_pawns_list.append(pawn)

    return len(safe_pawns_list)


Using Numpy

The following is less fast (at least for these small amounts of pawns), however the logic is arguably more clear.

def intersect2d(a1, a2):
    # https://stackoverflow.com/questions/8317022/get-intersecting-rows-across-two-2d-numpy-arrays
    rows, ncols = a1.shape
    dtype = {'names': ['f{}'.format(i) for i in range(ncols)],
             'formats': ncols * [a1.dtype]}

    a3 = np.intersect1d(a1.view(dtype), a2.view(dtype))

    # This last bit is optional if you're okay with "C" being a structured array...
    a3 = a3.view(a1.dtype).reshape(-1, ncols)
    return a3


def safe_pawns_np(pawns: set) -&gt; int:
    letters_string = "abcdefgh"

    np_pawns = np.array(list(map(lambda x: (letters_string.index(x[0]), int(x[1])), pawns)))

    safe_spots_right = np_pawns.copy()
    safe_spots_right[:, 0] += 1
    safe_spots_right[:, 1] += 1

    safe_spots_left = np_pawns.copy()
    safe_spots_left[:, 0] -= 1
    safe_spots_left[:, 1] += 1

    safe_spots = np.vstack((safe_spots_right, safe_spots_left))

    return len(intersect2d(np_pawns, safe_spots))