SHA256 password cracker - brute force
The optimization you are looking for is very simple:
Ask your self - what do I really need to keep?
The answer: only the password guess I am currently checking.
There is no need to store all the old guesses, so there is no need for files or huge lists.
All you really need is one string you will keep updating.
To visualize this, think of your string as a growing number, with each letter being a base 58 digit.
Now. all you really need is to do a +1 on the first digit, checking for carry and updating following digits if needed just like usual addition.
Unfortunately, Python strings don't allow assignment by index, but they do support slicing.
Here is a function that would generate sequential passwords running through all the letters and growing the length as needed (one password per call!):
def make_next_guess(guess):
    carry = 1
    next_guess = guess

    for i in range(len(guess)):
        cur_char = ord(guess[i]) + carry
        if cur_char &gt; ord('z'):
            cur_char = ord('A')
            carry = 1
        else:
            carry = 0

        next_guess = next_guess[:i] + chr(cur_char) + guess[i + 1:]
        if carry == 0:
             break

    if carry = 1:
        next_guess += 'A'

    return next_guess

With this you can use one loop for all the possibilities up to the maximum length:
guess = 'A'

for _ in range(58 ** 14): #password maximum length 14 and there are 58 characters that can be used
    
    if hash_password(guess) == secure_password:
        print(guess)
        break

    guess = make_next_guess(guess)