Python 3home |
Core principles
Here are the main components of a properly formatted program:
""" tip_calculator.py -- calculate tip for a restaurant bill
Author: David Blaikie dbb212@nyu.edu
Last modified: 9/19/2017
"""
import sys # part of Python distribution (installed with Python)
import pandas as pd # installed "3rd party" modules
import myownmod as mm # "local" module (part of local codebase)
# constant message strings are not required to be placed
# here, but in professional programs they are kept
# separate from the logic, often in separate "config" files
MSG1 = 'A {}% tip (${}) was added to the bill, for a total of ${}.'
MSG2 = 'With {} in your party, each person must pay ${}.'
# sys.argv[0] is the program's pathname (e.g. /this/that/other.py)
# os.path.basename() returns just the program name (e.g. other.py)
USAGE_STRING = "Usage: {os.path.basename(sys.argv[0])} [total amount] [# in party] [tip percentage]
def usage(msg):
""" print an error message, usage: string and exit
Args: msg (str): an error message
Returns: None (exits from here)
Raises: N/A (does not explicitly raise an exception)
"""
sys.stderr.write(f'Error: {msg}')
exit(USAGE_STRING)
def validate_normalize_input(args):
""" verify command-line input
Args: N/A (reads from sys.argv)
Returns:
bill_amt (float): the bill amount
party_size (int): the number of people
tip_pct (float): the percent tip to be applied, in 100’s
Raises: N/A (does not explicitly raise an exception)
"""
if not len(sys.argv) == 4:
usage('please enter all required arguments')
try:
bill_amt = float(sys.argv[1])
party_size = int(sys.argv[2])
tip_pct = float(sys.argv[3])
except ValueError:
usage('arguments must be numbers')
return bill_amt, party_size, tip_pct
def perform_calculations(bill_amt, party_size, tip_pct):
"""
calculate tip amount, total bill and person's share
Args:
bill_amount (float): the total bill
party_size (int): the number in party
tip_pct (float): the tip percentage in 100’s
Returns:
tip_amt (float): the tip in $
total_bill (float): the bill including tip
person_share (float): equal share of bill per person
Raises:
N/A (does not specifically raise an exception)
"""
tip_amt = bill_amt * tip_pct * .01
total_bill = bill_amt + tip_amt
person_share = total_bill / party_size
return tip_amt, total_bill, person_share
def report_results(pct, tip_amt, total_bill, size, person_share):
""" print results in formatted strings
Args:
pct (float): the tip percentage in 100’s
tip_amt (float): the tip in $
total_bill (float): the bill including tip
size (int): the party slize
person_share (float): equal share of bill per person
Returns:
None (prints result)
Raises:
N/A
"""
print(MSG1.format(pct, tip_amt, total_bill))
print(MSG2.format(size, person_share))
def main(args):
""" execute script
Args: args (list): the command-line arguments
Returns: None
Raises: N/A
"""
bill, size, pct = validate_normalize_input(args)
tip_amt, total_bill, person_share = perform_calculations(bill, size,
pct)
report_results(pct, tip_amt, total_bill, size, person_share)
if __name__ == '__main__': # 'main body' code
main(sys.argv[1:])
The code inside the if __name__ == '__main__' block is intended to be the call that starts the program. If this Python script is imported, the main() function will not be called, because the if test will only be true if the script is executed, and will not be true if it is imported. We do this in order to allow the script's functions to be imported and used without actually running the script -- we may want to test the script's functions (unit testing) or make use of a function from the script in another program. Whether we intend to import a script or not, it is considered a "best practice" to build all of our programs in this way -- with a "main body" of statements collected under function main(), and the call to main() inside the if __name__ == '__main__' gate. This structure will be required for all assignments submitted for the remainder of the course.
All professional programs should use the template.
The template calls for most code to appear inside of functions, and for the "start" of the program run (i.e., after imports and global variables are defined) to appear inside a function called main(). We then find at the bottom of the template a call to main(). This starts the program's main execution.
import sys
def main():
print('hello, world!')
print(f'you are running python {sys.version.split()[0]}')
if __name__ == '__main__':
main()
Understanding if __name__ == '__main__' This if statement in the "global" or "main body" space (meaning outside of any function), we may think of as a "module gate", featuring a test that will be True only if the script was run directly, and False if the script was imported as a module. This allows us to import the script and test individual functions without actually running the code. Conclusion What we are describing in this slide deck are commonly accepted industry practices for preparing scripts for development by a team, or for review by professional colleagues. It is a good idea to use them when sharing code with others, whether teammates, company colleagues, professional colleagues, or potential employers. It should be emphasized that "personal" scripts, i.e. scripts that aren't shared with others, are of a generally shorter length and aren't expected to be updated or extended on a regular basis, generally do not need to use the standard template or use type hints.