Chapter 3 Functions

3.1 Function Arguments and Returns

3.1.1 Data Types

Go back to fan’s Python Code Examples Repository (bookdown site) or the pyfan Package (API).

3.1.1.1 Check Parameter Type

There are parameters of a function, depending on the parameter type, execute the program differently. If integer, behave one way if string behave in another way.

# define the function
def get_speckey_dict(gn_speckey=None):
    if isinstance(gn_speckey, str):
        print(f'{gn_speckey=} is a string')
    elif isinstance(gn_speckey, int):
        print(f'{gn_speckey=} is an integer')
    else:
        raise TypeError(f'{gn_speckey=} was not a string or an integer')
# Call function with integer
get_speckey_dict(1)
# Call function with string
## gn_speckey=1 is an integer
get_speckey_dict('abc')
# Call function with a list
## gn_speckey='abc' is a string
try: 
    get_speckey_dict(['abc'])
except TypeError as e: 
    print(f'Exception{e=}')
## Exceptione=TypeError("gn_speckey=['abc'] was not a string or an integer")

3.1.1.2 Check if Parameter is String or Integer In Interval

There is a function that takes a string or an integer between certain values. Execute if either of these two conditions are satisfied, do not if neither is satisfied. Below, print if string or an int between 1 and 11.

# condition check function
def check_condition(gn_invoke_set):

  bl_is_str = isinstance(gn_invoke_set, str)
  bl_is_int = isinstance(gn_invoke_set, int)
  if bl_is_int:
    bl_between = (gn_invoke_set >= 1 and gn_invoke_set <= 11)
  else: 
    bl_between = False
    
  if bl_between or bl_is_str:
      print(f'{gn_invoke_set=}')
  else:
      print(f'{gn_invoke_set=} is not string or an integer between 1 and 11')
      
# call with string or integer
check_condition('string')
## gn_invoke_set='string'
check_condition(11)
## gn_invoke_set=11
check_condition(1)
## gn_invoke_set=1
check_condition(199)
## gn_invoke_set=199 is not string or an integer between 1 and 11
check_condition(['abc'])
## gn_invoke_set=['abc'] is not string or an integer between 1 and 11

3.1.2 Function Arguments

Go back to fan’s Python Code Examples Repository (bookdown site) or the pyfan Package (API).

import pprint

3.1.2.1 Mutable Argument Default

If a parameter is a list, that is mutable, or a dict. Default values for the mutable parameter should be None, with the actual default value provided inside the function.

# Mutable dict as parameters
def ffi_tab_txt2tex(dc_fmd_sym_sig=None, dc_tex_sig_sym=None):
    if dc_fmd_sym_sig is None:
        # defaults
        dc_fmd_sym_sig = {'***': 1e-2, '**': 5e-2, '*': 1e-1}
    if dc_tex_sig_sym is None:
        # defaults
        dc_tex_sig_sym = {'1e-3': '\\sym{***}',
                          '1e-2': '\\sym{**}',
                          '5e-2': '\\sym{*}',
                          '1e-1': '\\dagger'}

    # print
    print(f'{dc_fmd_sym_sig=}')
    print(f'{dc_tex_sig_sym=}')
    
# Call function with default
ffi_tab_txt2tex()
# Call function update the first dict 
## dc_fmd_sym_sig={'***': 0.01, '**': 0.05, '*': 0.1}
## dc_tex_sig_sym={'1e-3': '\\sym{***}', '1e-2': '\\sym{**}', '5e-2': '\\sym{*}', '1e-1': '\\dagger'}
ffi_tab_txt2tex(dc_fmd_sym_sig = {'***': 1e-3, '**': 1e-2, '*': 5e-2})
## dc_fmd_sym_sig={'***': 0.001, '**': 0.01, '*': 0.05}
## dc_tex_sig_sym={'1e-3': '\\sym{***}', '1e-2': '\\sym{**}', '5e-2': '\\sym{*}', '1e-1': '\\dagger'}

see “Least Astonishment” and the Mutable Default Argument.

3.1.2.2 Python Dictionary As Argument via kwargs

There is a python function that outputs a dictionary with key and value pairs that specify key aspects of how a model should be solved. For example, one of the parameters could specify the vcpu requirement. This vcpu requirement might change, and so it should be easy to update this key with alternative values.

These are accomplished in the following manner. Define the full key-value pair list, with default values for several dictionaries, with model simulation, support, and compute parameters for example. These lists could be updated with some default alternative combinations, or alternatively, it could be updated with externally provided dictionary with both updated values for existing keys, or even additional key value pairs.

First, we create a function that processes and outputs default parameters, it has two inputs, it_default_group to specify pre-fixed adjustments from defaults, and kwargs that allows for arbitrarily modifications and additions to parameter dictionary.

def gen_compesti_spec(it_default_group=None, **kwargs):
    # A. Define the default parameter keys and values
    esti_specs = {'esti_method': 'MomentsSimuStates',
                  'momsets_type': ['a', '20180805a'],
                  'esti_param_vec_count': 1,
                  'esti_max_func_eval': 10,
                  'graph_frequncy': 20}
    compute_specs = {'cpu': str(1024 * 1),
                     'memory': str(517),  # only need about 160 mb in reality
                     'workers': 1,
                     'aws_fargate': False}

    # B. For different
    compesti_specs = {**compute_specs, **esti_specs}

    # C. Update dictionaries with parameter group values
    if it_default_group == 1:
        compesti_specs_updates = {'memory': str(1024 * 55),
                                  'compute_param_vec_count': 6,
                                  'esti_param_vec_count': 640}
        compesti_specs.update(compesti_specs_updates)

    # D. Update with kward, could append new
    compesti_specs.update(kwargs)

    return compesti_specs

Second, we test the defaults:

compesti_specs = gen_compesti_spec()
pprint.pprint(compesti_specs, width=1)
## {'aws_fargate': False,
##  'cpu': '1024',
##  'esti_max_func_eval': 10,
##  'esti_method': 'MomentsSimuStates',
##  'esti_param_vec_count': 1,
##  'graph_frequncy': 20,
##  'memory': '517',
##  'momsets_type': ['a',
##                   '20180805a'],
##  'workers': 1}

Third, we test using default group 1, pre-fixed changes to defaults:

compesti_specs = gen_compesti_spec(it_default_group=1)
pprint.pprint(compesti_specs, width=1)
## {'aws_fargate': False,
##  'compute_param_vec_count': 6,
##  'cpu': '1024',
##  'esti_max_func_eval': 10,
##  'esti_method': 'MomentsSimuStates',
##  'esti_param_vec_count': 640,
##  'graph_frequncy': 20,
##  'memory': '56320',
##  'momsets_type': ['a',
##                   '20180805a'],
##  'workers': 1}

Fourth, we use kwargs to feed in arbitrary dictionary to update and append to existing parameter dictionary:

compesti_specs_updates = {'esti_method': 'MomentsSimuStateszzz',
                          'moments_type': ['a', '20180805azzz'],
                          'momsets_type': ['a', '20180805azzz'],
                          'momsets_type_uuu': ['a', '20180805azzz']}
compesti_specs = gen_compesti_spec(it_default_group=None, **compesti_specs_updates)
pprint.pprint(compesti_specs, width=1)
## {'aws_fargate': False,
##  'cpu': '1024',
##  'esti_max_func_eval': 10,
##  'esti_method': 'MomentsSimuStateszzz',
##  'esti_param_vec_count': 1,
##  'graph_frequncy': 20,
##  'memory': '517',
##  'moments_type': ['a',
##                   '20180805azzz'],
##  'momsets_type': ['a',
##                   '20180805azzz'],
##  'momsets_type_uuu': ['a',
##                       '20180805azzz'],
##  'workers': 1}

3.1.2.3 Named Argument List and Dictionary

Define a function with named and unnamed arguments:

def gen_compesti_spec_named(it_default_group, esti_method, memory=123, graph_frequncy=10):
    # A. Define the default parameter keys and values
    esti_specs = {'esti_method': 'MomentsSimuStates',
                  'momsets_type': ['a', '20180805a'],
                  'it_default_group': it_default_group,
                  'esti_param_vec_count': 1,
                  'esti_max_func_eval': 10,
                  'graph_frequncy': graph_frequncy}
    compute_specs = {'cpu': str(1024 * 1),
                     'memory': str(memory),  # only need about 160 mb in reality
                     'workers': 1,
                     'aws_fargate': False}

    # B. For different
    compesti_specs = {**compute_specs, **esti_specs}

    return compesti_specs

Provide inputs for the first two unnamed parameters explicitly. Then provided the two named parameters via a dictionary:

dc_inputs = {'memory':12345, 'graph_frequncy':2}
compesti_specs = gen_compesti_spec_named(None, 'MomentsSimuStates', **dc_inputs)
pprint.pprint(compesti_specs, width=1)
## {'aws_fargate': False,
##  'cpu': '1024',
##  'esti_max_func_eval': 10,
##  'esti_method': 'MomentsSimuStates',
##  'esti_param_vec_count': 1,
##  'graph_frequncy': 2,
##  'it_default_group': None,
##  'memory': '12345',
##  'momsets_type': ['a',
##                   '20180805a'],
##  'workers': 1}

3.1.3 Python Command-line Arguments Parsing

Go back to fan’s Python Code Examples Repository (bookdown site) or the pyfan Package (API).

import pprint
import argparse

3.1.3.1 Positional and Optional Arguments

Provide a positional and an optional argument. Position arguments are provided positions when the module is called, without prefixed by which parameter this is. Optional argument requires parameter specification.

# Start parser for arguments
parser = argparse.ArgumentParser()

# Positional argument 1st, will be stored as int
parser.add_argument('esrtype', type=int, help='positional argument 1st')
# Positional argument 2nd, will be stored as string
## _StoreAction(option_strings=[], dest='esrtype', nargs=None, const=None, default=None, type=<class 'int'>, choices=None, help='positional argument 1st', metavar=None)
parser.add_argument('speckey', type=str, help='positional argument 2nd')

# Optional argument
## _StoreAction(option_strings=[], dest='speckey', nargs=None, const=None, default=None, type=<class 'str'>, choices=None, help='positional argument 2nd', metavar=None)
parser.add_argument('-A', type=str, default='opt_arg_A_default_str_val')

# Call with positional argument specified
# Note that one is bracketed, will be interpreted as int
## _StoreAction(option_strings=['-A'], dest='A', nargs=None, const=None, default='opt_arg_A_default_str_val', type=<class 'str'>, choices=None, help=None, metavar=None)
print(f"Must specify posi. arg: {parser.parse_args(['1', 'mpoly_1=esti_tinytst_mpoly_13=3=3'])=}")
# Call with two positional arguments and one optional
# Note that the first positional argument becomes int, second beomce str
## Must specify posi. arg: parser.parse_args(['1', 'mpoly_1=esti_tinytst_mpoly_13=3=3'])=Namespace(A='opt_arg_A_default_str_val', esrtype=1, speckey='mpoly_1=esti_tinytst_mpoly_13=3=3')
print(f"With opt arg: {parser.parse_args(['1', '2', '-A', 'abc'])=}")
## With opt arg: parser.parse_args(['1', '2', '-A', 'abc'])=Namespace(A='abc', esrtype=1, speckey='2')

3.1.3.2 Short and Long Parameter Name Specifications

Test below a boolean parameter that will be true or false. The default value is False. The parameter is called boolparam with short name abc. There is a variety of ways of setting the parameter to true.

# Start parser for arguments
parser = argparse.ArgumentParser()

# short name for the first parameter is a, full name is abc, boolean parameter
parser.add_argument('-abc', '--boolparam', action="store_true", default=False)

# default is false but turn on option so true
## _StoreTrueAction(option_strings=['-abc', '--boolparam'], dest='boolparam', nargs=0, const=True, default=False, type=None, choices=None, help=None, metavar=None)
print(f"default false: {parser.parse_args()=}")
## default false: parser.parse_args()=Namespace(boolparam=False)
print(f"default false, set to true, short all: {parser.parse_args(['-abc'])=}")
## default false, set to true, short all: parser.parse_args(['-abc'])=Namespace(boolparam=True)
print(f"default false, set to true, short part ab for abc: {parser.parse_args(['-ab'])=}")
## default false, set to true, short part ab for abc: parser.parse_args(['-ab'])=Namespace(boolparam=True)
print(f"default false, set to true, short part a for abc: {parser.parse_args(['-a'])=}")
## default false, set to true, short part a for abc: parser.parse_args(['-a'])=Namespace(boolparam=True)
print(f"default false, set to true, full param: {parser.parse_args(['--boolparam'])=}")
## default false, set to true, full param: parser.parse_args(['--boolparam'])=Namespace(boolparam=True)
print(f"default false, set to true, full param: {parser.parse_args(['--boolparam'])=}")
## default false, set to true, full param: parser.parse_args(['--boolparam'])=Namespace(boolparam=True)

3.1.3.3 A List of Allowed Values

There is a parameter, only some specific values are allowed. Also provide help for each allowed option. Note added argparse.RawTextHelpFormatter to parse the next lines in help.

# Start parse
parser = argparse.ArgumentParser(description='Run ESR cmd', formatter_class=argparse.RawTextHelpFormatter)

# A required positional argument parameter tht is int and can take eight possible values
parser.add_argument('esrtype', type=int,
                    choices=[1, 2, 3, 4, 5, 6, 7, 8],
                    help='1. Simulate at N sets of parameter combinations\n'
                         '2. Polynomial approximation surface based on (1) '
                         'for each outcome of interest, find best\n'
                         '3. Estimation at N sets of starting points with (2) as objective function\n'
                         '4. Gather results frorm (3), find M best.\n'
                         '5. Simulate (estimate once) at the top M best results from (4) actual model, '
                         'compare objective to approximated from (3)\n'
                         '6. Gather results from (5), re-rank best of the M best from (4)\n'
                         '7. Estimate at the top M best results from (4) actual model, '
                         '(4) M best are M best seeds\n'
                         '8. Gather results from (7), re-rank best of the final results from the M best seeds')
# Print defaults
## _StoreAction(option_strings=[], dest='esrtype', nargs=None, const=None, default=None, type=<class 'int'>, choices=[1, 2, 3, 4, 5, 6, 7, 8], help='1. Simulate at N sets of parameter combinations\n2. Polynomial approximation surface based on (1) for each outcome of interest, find best\n3. Estimation at N sets of starting points with (2) as objective function\n4. Gather results frorm (3), find M best.\n5. Simulate (estimate once) at the top M best results from (4) actual model, compare objective to approximated from (3)\n6. Gather results from (5), re-rank best of the M best from (4)\n7. Estimate at the top M best results from (4) actual model, (4) M best are M best seeds\n8. Gather results from (7), re-rank best of the final results from the M best seeds', metavar=None)
print(f"provide 1 for the value of the positional argument: {parser.parse_args(['1'])=}")
## provide 1 for the value of the positional argument: parser.parse_args(['1'])=Namespace(esrtype=1)

3.1.3.4 Boolean, Integer, String and list Parameters

How to handle parameters of different types, boolean, integer, string and list. For these four types, the same way to specify short and long parameter names. How to set the parameter types, and how to set default values for each type.

# Start parser for arguments
parser = argparse.ArgumentParser()

# Single letter string parameters
# Note dest name over-rides full name
parser.add_argument('-cta', '--cttaaaaa', dest="combo_type_a", default='e',
                    type=str)
# Multiple letters and integers
# Note without dest full name is dest
## _StoreAction(option_strings=['-cta', '--cttaaaaa'], dest='combo_type_a', nargs=None, const=None, default='e', type=<class 'str'>, choices=None, help=None, metavar=None)
parser.add_argument('-ctb', '--combo_type_b', default='20201025',
                    type=str)
# Multiple letters and integers
# Note without dest and full name short name is parameter name
## _StoreAction(option_strings=['-ctb', '--combo_type_b'], dest='combo_type_b', nargs=None, const=None, default='20201025', type=<class 'str'>, choices=None, help=None, metavar=None)
parser.add_argument('-ctc', default=['list_tKap_mlt_ce1a2'],
                    nargs='+', type=str)

# Print defaults
## _StoreAction(option_strings=['-ctc'], dest='ctc', nargs='+', const=None, default=['list_tKap_mlt_ce1a2'], type=<class 'str'>, choices=None, help=None, metavar=None)
print(f"default false: {parser.parse_args()=}")
# change parameters
## default false: parser.parse_args()=Namespace(combo_type_a='e', combo_type_b='20201025', ctc=['list_tKap_mlt_ce1a2'])
print(f"default false: {parser.parse_args(['-ctb', '20201111'])=}")
## default false: parser.parse_args(['-ctb', '20201111'])=Namespace(combo_type_a='e', combo_type_b='20201111', ctc=['list_tKap_mlt_ce1a2'])

see variable-argument-lists.

3.1.3.5 Parse multiple parameter types

Provide several types of parameters to a function, so that the function can be called easily container call to execute estimation. The types of parameters includes:

  1. A list including parameter information
  2. A string including estimation/computational controls
  3. Additional parameters
# Start parser for arguments
parser = argparse.ArgumentParser()

# First (and only) positional argument for esrtype:
parser.add_argument('esrtype', type=int, help='positional argument')

# Optional argument
## _StoreAction(option_strings=[], dest='esrtype', nargs=None, const=None, default=None, type=<class 'int'>, choices=None, help='positional argument', metavar=None)
parser.add_argument('-s', dest='speckey', type=str,
                    default='ng_s_t=esti_tinytst_thin_1=3=3',
                    help="compute and esti keys and omments")

# abc and e of comb_type
## _StoreAction(option_strings=['-s'], dest='speckey', nargs=None, const=None, default='ng_s_t=esti_tinytst_thin_1=3=3', type=<class 'str'>, choices=None, help='compute and esti keys and omments', metavar=None)
parser.add_argument('-cta', dest="combo_type_a", default='e', type=str)
## _StoreAction(option_strings=['-cta'], dest='combo_type_a', nargs=None, const=None, default='e', type=<class 'str'>, choices=None, help=None, metavar=None)
parser.add_argument('-ctb', dest="combo_type_b", default='20201025', type=str)
## _StoreAction(option_strings=['-ctb'], dest='combo_type_b', nargs=None, const=None, default='20201025', type=<class 'str'>, choices=None, help=None, metavar=None)
parser.add_argument('-ctc', dest="combo_type_c", default=['list_tKap_mlt_ce1a2'], nargs='+', type=str)
## _StoreAction(option_strings=['-ctc'], dest='combo_type_c', nargs='+', const=None, default=['list_tKap_mlt_ce1a2'], type=<class 'str'>, choices=None, help=None, metavar=None)
parser.add_argument('-cte1', dest="combo_type_e1", default=None, type=str)
## _StoreAction(option_strings=['-cte1'], dest='combo_type_e1', nargs=None, const=None, default=None, type=<class 'str'>, choices=None, help=None, metavar=None)
parser.add_argument('-cte2', dest="combo_type_e2", default='mpoly_1=esti_tinytst_mpoly_13=3=3', type=str)

# other parameters
## _StoreAction(option_strings=['-cte2'], dest='combo_type_e2', nargs=None, const=None, default='mpoly_1=esti_tinytst_mpoly_13=3=3', type=<class 'str'>, choices=None, help=None, metavar=None)
parser.add_argument('-f', dest="save_directory_main", default='esti')

# Default, must specify erstype
## _StoreAction(option_strings=['-f'], dest='save_directory_main', nargs=None, const=None, default='esti', type=None, choices=None, help=None, metavar=None)
print(f"default false: {parser.parse_args(['1'])=}")
# Print with the nargs+ arguments
# specified two elements, abc, and efg for nargs ctc, becomes a string list
## default false: parser.parse_args(['1'])=Namespace(combo_type_a='e', combo_type_b='20201025', combo_type_c=['list_tKap_mlt_ce1a2'], combo_type_e1=None, combo_type_e2='mpoly_1=esti_tinytst_mpoly_13=3=3', esrtype=1, save_directory_main='esti', speckey='ng_s_t=esti_tinytst_thin_1=3=3')
print(f"default false: {parser.parse_args(['1', '-ctc', 'abc', 'efg'])=}")
# one input for ctc, still generates a list
## default false: parser.parse_args(['1', '-ctc', 'abc', 'efg'])=Namespace(combo_type_a='e', combo_type_b='20201025', combo_type_c=['abc', 'efg'], combo_type_e1=None, combo_type_e2='mpoly_1=esti_tinytst_mpoly_13=3=3', esrtype=1, save_directory_main='esti', speckey='ng_s_t=esti_tinytst_thin_1=3=3')
print(f"default false: {parser.parse_args(['1', '-ctc', 'abc'])=}")
## default false: parser.parse_args(['1', '-ctc', 'abc'])=Namespace(combo_type_a='e', combo_type_b='20201025', combo_type_c=['abc'], combo_type_e1=None, combo_type_e2='mpoly_1=esti_tinytst_mpoly_13=3=3', esrtype=1, save_directory_main='esti', speckey='ng_s_t=esti_tinytst_thin_1=3=3')

3.1.4 Function Returns

Go back to fan’s Python Code Examples Repository (bookdown site) or the pyfan Package (API).

3.1.4.1 Function with Multiple Optional Returns

There is a function that is already written, that returns some string value. Without interrupting the existing function, now add an additional return for the function. There is some conditional statement that controls whether the function returns one or two value.

In the example below, if the path contains

Create a testing function:

def get_val(spn_path):
    if 'G:' in spn_path:
        # this returns a tuple of length 2
        return spn_path, 'G:/repos'
    else:
        return spn_path

Call the function with one return, in the two calls below, the first call returns a tuple

# Return tuple
tp_st_return = get_val("G:/Dropbox (UH-ECON)")
# Unpack tuple return
st_path_a, st_path_b = get_val("G:/Dropbox (UH-ECON)")
# Single element return
st_return = get_val("C:/Dropbox (UH-ECON)")
# Print
print(f'{tp_st_return=} and {st_return=}')
## tp_st_return=('G:/Dropbox (UH-ECON)', 'G:/repos') and st_return='C:/Dropbox (UH-ECON)'
print(f'{st_path_a=} and {st_path_b=}')
## st_path_a='G:/Dropbox (UH-ECON)' and st_path_b='G:/repos'
print(f'{isinstance(tp_st_return, str)=}')
## isinstance(tp_st_return, str)=False
print(f'{isinstance(tp_st_return, tuple)=}')
## isinstance(tp_st_return, tuple)=True
print(f'{isinstance(st_return, str)=}')
## isinstance(st_return, str)=True
print(f'{isinstance(st_return, tuple)=}')
## isinstance(st_return, tuple)=False

3.2 Exceptions

3.2.1 Exception Handling

Go back to fan’s Python Code Examples Repository (bookdown site) or the pyfan Package (API).

3.2.1.1 A function That Raises an Error with Try Statement Catching it

Below, we have a function that will raise a TypeError unless we provide an integer input. The function is called with integer input and then called with a string input. The string input is wrapped in a try and except call, where the exception catches the TypeError and prints it.

# define the function
def ffi_error_test(gn_speckey=None):
    if isinstance(gn_speckey, int):
        print(f'{gn_speckey=} is an integer')
    else:
        raise TypeError(f'{gn_speckey=} is not an integer')
# Call function with integer
error_test(1)
# Call function with string
## gn_speckey=1 is an integer
try:
    ffi_error_test('abc')
except TypeError as error:
    print('Caught this error: ' + repr(error))
## Caught this error: TypeError("gn_speckey='abc' is not an integer")

3.2.1.2 Catch an Exception Noisily

Rather than only catching the last element of the exception, show the full trace. Notice in the example below, the second element of the loop does not work as function input. With exception catching, the loop continued despite the second element not working. The traceback shows more details at the end with full trace of the exception from the second element of the list as input for ffi_error_test().

import traceback
import numpy as np

ls_ob_inputs = [2, ['abc','efg'], 1, 123]

for ob_input in ls_ob_inputs:
    print(f'try input {ob_input=} with the error_test function:')
    try:
        ffi_error_test(ob_input)
    except TypeError as error:
        traceback.print_exc()
        print('Caught this error: ' + repr(error))
## try input ob_input=2 with the error_test function:
## gn_speckey=2 is an integer
## try input ob_input=['abc', 'efg'] with the error_test function:
## Caught this error: TypeError("gn_speckey=['abc', 'efg'] is not an integer")
## try input ob_input=1 with the error_test function:
## gn_speckey=1 is an integer
## try input ob_input=123 with the error_test function:
## gn_speckey=123 is an integer
## 
## Traceback (most recent call last):
##   File "<string>", line 4, in <module>
##   File "<string>", line 5, in ffi_error_test
## TypeError: gn_speckey=['abc', 'efg'] is not an integer

see How to print the stack trace of an exception object in Python?

3.2.1.3 Handle Parameters When Conditions Not Satisfied

There is a function, that can estimate or simulate, under both functionalities, there is a common string parameter, that requires specifying estimation or simulation conditions. The common string parameter should be a simple string without special separators in the case of simulation, and should be four strings concatenated together with equal sign for estimation. Generate an exception if the function is called for estimation but the string parameter does not have the required structure.

# ls_st_spec_key_dict = ['NG_S_D', 'NG_S_D=KAP_M0_NLD_M_SIMU=2=3']
# st_connector = '='
# ls_st_esti_simu = ['esti', 'simu']
# for st_spec_key_dict in ls_st_spec_key_dict:
#   for st_esti_simu in ls_st_esti_simu:
#     if st_esti_simu == 'simu':
#       if len(st_spec_key_dict.split(st_connector)) and
#         print('simulate with ' + st_spec_key_dict)

if estimate and not isinstance(spec_key_dict, str):
    pass
elif (estimate is False and isinstance(spec_key_dict, str)) or (estimate is False and isinstance(spec_key_dict, dict)):
    pass
else:
    st_error = 'speckey=' + speckey + ' and estimate=' + str(estimate)
    raise ValueError(st_error)

3.2.1.4 Proceed Despite Error

Sometimes, code should proceed despite error, to finish a loop for example:

# estimate at each initial random points
for it_esti_ctr in range(esti_param_vec_count):
    # Update the 3rd element of combo_type, which determines which draw index to use
    combo_type[3] = it_esti_ctr
    try:
        invoke_run_main.invoke_main(combo_type, **dc_invoke_main_args)
    except Exception:
        logging.critical(f'Finished this {it_esti_ctr=} of {range(esti_param_vec_count)=}')