Python 3

home

Introduction to Python

davidbpython.com




User-Defined Function Variable Scoping

variable name scoping: the local variable

Variable names initialized inside a function are local to the function.


def myfunc():
    a = 10
    return a

var = myfunc()
print(var)          # 10
print(a)            # NameError ('a' does not exist here)





variable name scoping: the global variable

Any variable defined outside a function is global.


var = 'hello global'

def myfunc():
    print(var)

myfunc()                  # hello global





"pure" functions

Functions that do not touch outside variables, and do not create "side effects" (for example, calling exit(), print() or input()), are considered "pure" -- and are preferred.


"Pure" functions have the following characteristics:





"pure" functions: working only with "inside" (local) variables

"Outside" (Global) variables are ones defined outside the function -- they should be avoided.


wrong way: referring to an outside variable inside a function

val = '5'                   # defined outside any function

def doubleit():
    dval = int(val) * 2     # BAD:  refers to "global" variable 'val'
    return dval

new_val = doubleit()

right way: passing outside variables as arguments

val = '5'                   # defined outside any function

def doubleit(arg):
    dval = int(arg) * 2     # GOOD:  refers to same value as 'val',
    return dval             #        but accessed through local
                            #        argument 'arg'

new_val = doubleit(val)     # passing variable to function -
                            #   correct way to get a value into the function





"pure" functions: avoiding "side-effects"

print(), input(), exit() all "touch" the outside world and in many cases should be avoided inside functions.



Although it is of course possible (and sometimes practical) to use these built-in functions inside our function, we should avoid them if we are interested in making a function "pure".





"pure" functions: why prefer them?

Here are some positive reasons to strive for purity.


You may notice that these "impure" practices do not cause errors. So why should we avoid them?


Please note that during development it is perfectly allowable to print(), exit() or input() from inside a function. We may also decide on our own that this is all right in shorter programs, or ones that we working on in isolation. It is with longer programs and collaborative programs where purity becomes more important.





"pure" functions: using 'raise' instead of exit() inside functions

exit() should not be called inside a function.


def doubleit(arg):
    if not arg.isdigit():
        raise ValueError('arg must be all digits')   # GOOD:  error signaled with raise
    dval = int(arg) * 2
    return dval

val = input('what is your value? ')
new_val = doubleit(val)





signalling errors (exceptions) with 'raise'

'raise' creates an error condition (exception) that usually terminates program execution.



To raise an exception, we simply follow raise with the type of error we would like to raise, and an optional message:

raise IndexError('I am now raising an IndexError exception')

You may raise any existing exception (you may even define your own). Here is a list of common exceptions:

Exception TypeReason
TypeError the wrong type used in an expression
ValueError the wrong value used in an expression
FileNotFoundError a file or directory is requested that doesn't exist
IndexError use of an index for a nonexistent list/tuple item
KeyError a requested key does not exist in the dictionary





global variables and function "purity"

Globals should be used inside functions only in select circumstances.


STATE_TAX = .05    # ALL CAPS designates a "constant"


def calculate_bill(bill_amount, tip_pct):

    tax = bill_amount * STATE_TAX     # int, 5
    tip = bill_amount * tip_pct       # float, 20.0

    total_amount = bill_amount + tax + tip   # float, 125.0

    return total_amount


total = calculate_bill(100, .20)      # float, 125.0





the four variable scopes: l-e-g-b

Four kinds of variables: (L)ocal, (E)nclosing, (G)lobal and (B)uiltin.


filename = 'pyku.txt'        # 'filename':  global

                                # 'get_text':  global (function name is a
                                #                      variable as well)
def get_text(fname):            # 'fname':     local
    fh = open(fname)            # 'fh':        local; 'open':  builtin
    text = fh.read()            # 'text':      local
    return text

txt = get_text(filename)        # 'txt':       global
print(txt)                      # 'print':     builtin





proper code organization

Core principles.


Here are the main components of a properly formatted program:


See the tip_calculator.py file in your files directory for an example and notes below.





[pr]