[Tutor] functions first?
Steven D'Aprano
steve at pearwood.info
Tue Jan 27 12:18:35 CET 2015
On Mon, Jan 26, 2015 at 06:04:10PM -0800, Alex Kleider wrote:
> Please correct me if I am wrong, but I've assumed that it is proper to
> define all functions before embarking on the main body of a program.
> I find myself breaking this rule because I want to set the default
> values of some named function parameters based on a configuration file
> which I have to first read, hence the need to break the rule.
> ..so my question is "is this acceptable" or "is there a better way?"
I'm not really sure I understand the question correctly.
As far as *design* go, there are two competing paradigms, "top-down"
versus "bottom-up". Perhaps you are thinking about that?
In top-down, you start with the part of the application closest to the
user, say, a web browser:
def run_browser():
win = make_window()
page = get_url(whatever)
show_page(page, win)
Then you write the next level down:
def make_window()
...
def get_url(address):
...
def show_page(page, win):
...
and so on, all the way down to the most primitive and simple parts of
the application:
def add_one(n):
return n+1
In bottom-up programming, you do the same, only in reverse. You build
the most primitive functions first, then add them together like Lego
blocks, getting more and more complicated and powerful until you end up
with a fully-functioning web browser.
Personally, I think that in any real application, you need a combination
of both top-down and bottom-up, but mostly top-down. (How do you know
what primitive operations you will need right at the start of the design
process?) Also, many of the primitive "Lego blocks" already exist for
you, in the Python standard library.
If you are talking about the order in which functions appear in the
source file, I tend to write them like this:
=== start of file ===
hash-bang line
encoding cookie
licence
doc string explaining what the module does
from __future__ imports
import standard library modules
import custom modules
metadata such as version number
other constants
global variables
custom exception types
private functions and classes
public functions and classes
main function
if __name__ == '__main__' code block to run the application
=== end of file ===
All of those are optional (especially global variables, which I try hard
to avoid). Sometimes I swap the order of private and public sections,
but the main function (if any) is always at the end just before the "if
__name__" section.
As far as the default values go, I *think* what you are doing is
something like this:
f = open("config")
value = int(f.readline())
f.close()
def function(x, y=value):
return x + y
Am I right?
If so, that's not too bad, especially for small scripts, but I think a
better approach (especially for long-lasting applications like a server)
might be something like this:
def read_config(fname, config):
f = open(fname)
config['value'] = int(f.readline())
f.close()
PARAMS = {
# set some default-defaults that apply if the config file
# isn't available
value: 0,
}
read_config('config', PARAMS)
def func(x, y=None):
if y is None:
y = PARAMS['value']
return x + y
This gives lots of flexibility:
- you can easily save and restore the PARAMS global variable with
just a dict copy operation;
- you can apply multiple config files;
- you can keep multiple PARAMS dicts (although as written, func only
uses the one named specifically PARAMS);
- read_config can be isolated for testing;
- you can re-read the config file without exiting the application;
etc.
Does this help?
--
Steve
More information about the Tutor
mailing list