packages, globals, and __builtin__

David Bolen db3l at fitlinxx.com
Fri May 11 19:23:14 EDT 2001


Laura Creighton <lac at cd.chalmers.se> writes:

(...)
> My client -- not part of the package:
> 
> 	import Tkinter
> 	import Caps.Widgets		
> 
> 	[... about 10 lines of not related stuff here ...]
> 	root = Tkinter.Tk()
> 	userPrefs = Caps.Widgets.UserPreferences()
> 
> 	import __builtin__
> 	__builtin__.up = Caps.Widgets.UserPreferences()
> 
> 	controlMediator = Caps.Widgets.ControlMediator(userPrefs)
> 	Tkinter.tkinter.createfilehandler(bl, Tkinter.READABLE, bl_handler)
> 	root.mainloop()
> 
> (my main client which doesn't live in the Caps directories)
> 
> and then the Caps Widgets.  Currently they all get passed userPrefs
> as a parameter, and I really don't want this.

One question I have in the above is whether the fact that you actually
instantiate two instances of your UserPreferences() object is
necessary - you've got one you're passing amongst your controls and
one you store in __builtin__.up.

> This is (some of) the file Caps/Widgets/UserPreferences.py
> 
> class UserPreferences:
>     
>     def __init__(self):
>         # Trust me on this.  You don't want to really see the dictionary
>         self.prefs = { # Pretend something reasonable goes here }
>         
>     def getPreferences(self, key):
>         return self.prefs[key]

(...)

> Then I do things now like this:
> 
> 	a, b, c, d = self.userPrefs.getPreferences(whatever)
> 
> which works because I am passing around userPrefs as a parameter.
> 
> I don't want to make a new userpref object in the __init__ of every
> Widget. I just want one global reference I can get at whenever I like.
> And I thought that this was something that python simply refused to
> give me.

I think there are several possibilities, depending on what you really
want to do.  From the above, let's assume that you want your
UserPreferences module to handle preferences, but just for your own
package.  They should be accessible to clients as an interface to
package preferences, but the rest of your package will simply use the
preferences implicitly.  If you want the preferences to be an
outside-package sort of global configuration, then passing it to
package object constructors may be more appropriate.

What I would suggest is that you implement your UserPreferences module
such that it provides access to a single object instance.  Something
like (in UserPreferences.py):

    class _UserPreferences:
	pass

    preferences = _UserPreferences()

This will have the effect that the first import (from anyone) of
Caps.Widget.UserPreferences will instantiate the _UserPreferences
object, making it accessible as Caps.Widget.UserPreferences.preferences.

Alternatively, it's not clear from the above why your preferences have
to be a class - you could probably just make UserPreferences a module
with its preferences stored as module attributes and module functions
for manipulating them.  Then the module itself is implicitly your
singleton preferences instance.  (That's what I had referred to in my
prior posting)

Perhaps one key item here is to realize that when a module is
imported, it's actually being executed (the first time).  Thus you
don't have to just have definitions, but can create object instances,
perform actions, etc...

Given the above, anyone (whether a client or your own packages) that
wants to access the preferences, just needs to use that reference.
Your own package modules (which are at the level of Caps.Widget) can
just add the line "import UserPreferences" and access
UserPreferences.preferencesor or perhaps do "from UserPreferences
import preferences" and then just use "preferences".  Of course,
adjust names to please.

If you'd rather that clients of your package see "preferences" as
directly at the package layer, you can move the instantiation of the
class into your __init__.py (or just have your __init__ import that
instance locally - in fact that will happen given your __init__ below).

> Is it my __init.py__ file that is no good? This is Caps/Widgets/__init.py__
>
> 	import os
> 	import re
> 
> 	dir = __path__[0]
> 	for _file in os.listdir(_dir):
> 	    if _file != '__init__.py':
> 	        _m = re.match('(.*)\.py$', _file)
>         	if _m != None:
> 	            exec('from ' + _m.group(1) + ' import *')

It's fine as long as you intend to import everything (sans _ prefixed
stuff) from each module in your package into your main module
namespace.  I might use fnmatch rather than a regex to do the file
matching, but that's to taste.

But in your current scheme, for the UserPreferences module, all you're
doing is importing a reference to the UserPreference class into your
package namespace - without something (either that module itself or
your package initialization) instantiating that class, you are going
to be dependent on whomever does instantiate it in (in your case
currently, the top level client code) passing it down to other users.

(I suppose you could have a single function in your package that is
used to set a preferences pointer which would be kept at the global
level of the package file but that's not quite as clean I don't
think).

> Some day I want to make it do lazy loading, but now now.

You could always borrow a lazy module (say, from DateTime) and just
use that :-)

--
-- David
-- 
/-----------------------------------------------------------------------\
 \               David Bolen            \   E-mail: db3l at fitlinxx.com  /
  |             FitLinxx, Inc.            \  Phone: (203) 708-5192    |
 /  860 Canal Street, Stamford, CT  06902   \  Fax: (203) 316-5150     \
\-----------------------------------------------------------------------/



More information about the Python-list mailing list