A function is a named or callable program unit that performs an operation and can return a result. It gives other code a clear interface for using that operation, much like a small service inside an application. section 3.2
Useful functions do one thing well. Returning a value instead of printing inside the function keeps the operation reusable: callers can print the result, place it in a web page, store it in a data structure, or convert it to JSON. section 3.2.4
Design functions with these practical habits:
- Give the function a meaningful name that communicates its operation and fits the surrounding namespace. section 2.3.5
- Choose parameters that let callers control important variations without forcing them to duplicate conditional logic. section 2.3.5
- Return computed data rather than combining computation with output or other unrelated side effects. section 3.2.4
- Keep the function short enough to understand; when it becomes difficult to hold in context, refactor functionality into cooperating functions or a class. section 3.2.5
From output routine to reusable operation
A routine that builds a full name and prints it forces every caller to expect console output and makes testing awkward. A function that builds and returns the name lets each caller decide what to do with the result, while preserving a focused responsibility. section 3.2.4
def full_name(fnameNone, mname=None, lname=None) -> str:
"""Concatenates the names together and returns the full name
Arguments:
fname {str} -- first name
mname {str} -- middle name
lname {str} -- last name
Returns:
str -- the full name with only a single space between names
"""
full_name = " ".join(name for name in [fname, mname, lname] if name)
return full_name
Inspect a focused function that accepts optional name parts, joins available values, and returns the resulting string. section 3.2.4



















