One of the great things about the finally block is that it guarantees an operation is executed when we exit the scope of the try-catch block. We are forced to exit the scope when we return from the function. So, before we return we do the guaranteed task in the finally block first. If we didn't do this task, there can be bad consequences. For Eg :
Assume I create a connection in try block and I close it in finally block. If I return the connection in the catch block without closing it in the finally block, the function caller can start operating on a supposedly dead connection.
Hence, as you already know, if we didn't have return :
def test():
    try:
        1 / 0
    except Exception as exc:
        print("hi")
    finally:
        print("bye)
print(test())
The output will be
hi
bye