The other answers have already covered what can be improved in your script so I will not repeat any of that. Just wanted to add an alternative I found interesting.
An elimination-based approach:
def f(my_string):
brackets = ['()', '{}', '[]']
while True:
if any(x in my_string for x in brackets):
for br in brackets:
my_string = my_string.replace(br, '')
else:
if my_string:
return False
else:
return True
In every iteration the innermost brackets get eliminated (replaced with empty string). If we end up with an empty string, our initial one was balanced otherwise, not.
Examples and notes:
s = '[](){}{}()[]'-> reduces to nothing in onewhileiteration.s = '({[[{()}]]})'-> same length string requires 6 iterations (destroyed inside out)
You could add some short-circuiting for catching cases of lengty strings that have an uneven number of opening and closing brackets so that you don't waste your time but...