On 2015-01-27 03:18, Steven D'Aprano wrote:
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?
Yes, this is indeed what I was doing (but then on reflection,
became uncertain about the wisdom of doing it this way- hence
the question.)


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?


Yes, very much so.
Thank you again!

I use the docopt module to collect command line options and then configparser to read a file. Some of the values (such as a port number, for example) must then be adjusted. An example is a port number which I want to convert from "5022" to ":5022" if it's a non standard port or to "" if it is the standard "22" so it can then be used as a string format parameter. Perhaps I should be doing this in the same place as I set up the string rather than where
I populate the 'config' and 'PARAMS' dictionaries?
Sincerely,
Alex

_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to