Basic Collatz program with input validation in Python In chapter 3 of Automate the Boring Stuff with Python by Al Sweigart, one exercise asks to write a program which performs the Collatz sequence.
I wrote a basic version first, then added and restructured some of it to account for an invalid input. Any thoughts or criticisms to make it more concise, organized or legible would be appreciated.
def collatz(number): # This evaluates and prints the sequence of numbers.
        if number % 2 == 0: # If it's an even number.
            print(number // 2)
            return number // 2
        else: # If it's an odd number.
            print(3 * number + 1)
            return 3 * number + 1
        
def main(): # The main loop inside a function so the ValueError message can appear as many times as needed.
    try:
        user_input = int(input())
        while user_input &gt; 1:
            user_input = collatz(user_input)
        if user_input &lt;= 0: # If a negative integer is entered.
            print('Please enter a positive integer.')
        else:
            print('Enter another integer.')

    except ValueError: # If something other than an integer is entered.
        print('Please enter a valid integer.')

print('Type any positive integer.') # Main prompt.
        
while True: # This calls the main loop so you can repeat the equation as many times as needed.
    main()
Most of your code deals with obtaining and validating user input. That's
tedious code to write and maintain. In addition, programs premised on
user-interactivity are difficult to execute during development, testing, and
debugging: every execution requires a manual dialogue between the user (you,
the programmer) and the computer. That get annoying very fast. Moreover, your
program provides no clean way to quit. A more powerful approach based on
command-line arguments and the built-in argparse library is shown below.
The second issue is that your collatz() function intermixes algorithmic
computation and reporting (i.e. printing). That's almost
always a bad idea. A more flexible approach is to restrict collatz() purely to
the algorithm and do the printing at a higher level in the program -- either in
main(), if the reporting remains extremely simple, or in a function called by it.
The essence of most of these suggestions is to organize your code more
carefully to keep different tasks more separated: user input collection; input
validation; computation; and reporting.
import argparse
import sys

def main(args):
    # Get user arguments.
    opts = parse_args(args)

    # Make computations.
    sequences = [list(collatz(val)) for val in opts.initial_values]

    # Report to user. Enhance as needed.
    for seq in sequences:
        print(seq)

def parse_args(args):
    # You want the user to provide one or more integers
    # on the command line. The argparse library will handle that
    # automatically for many use cases (e.g, any integer). In your situation,
    # you do need to write a type validation function, but that process
    # is a bit simpler and more focused than your previous validation
    # code, which was mixed with the rest of the program's logic.
    ap = argparse.ArgumentParser()
    ap.add_argument('initial_values', nargs = '+', type = positive_int)
    return ap.parse_args(args)

def positive_int(val):
    # You can make this more robust if needed (eg to prevent values like
    # '12.34' from being accepted), but here's the basic technique.
    try:
        n = int(val)
        1 / n
        return n
    except Exception:
        raise argparse.ArgumentTypeError(f'Invalid positive integer: {val}')

def collatz(n):
    # Relieved of printing, this function becomes simpler, more testable,
    # and more general purpose.
    while n &gt; 1:
        yield n
        if n % 2 == 0:
            n = n // 2
        else:
            n = 3 * n + 1
    yield n

# Invoke main() if the program was executed rather than imported.
# This is important for testability, because testing tools need
# to import your code without executing the script in its entirety.
if __name__ == '__main__':
    main(sys.argv[1:])