Inserting class namespace into method scope

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sat Nov 20 19:59:40 EST 2010


On Sun, 21 Nov 2010 08:59:30 +1100, Ben Finney wrote:

> C'mon, Steven, you know the drill. If you want us to help you solve a
> problem, don't start with “I want to use this behaviour that seems
> loony, and I won't say why”. Instead, help us by telling us what problem
> you're trying to solve.

Well, I tried the TL;DR version. Now you have to read the full version.

Inspired by the last maxim of the Zen:

"Namespaces are one honking great idea -- let's do more of those!"

I'm looking to apply this to my code. I have a bunch of related functions 
and objects which are private to a module, with a single public function 
that acts as the API to those functions. I'd like to have these private 
functions in a namespace, separate from the rest of the module. Ignore 
the fact that they are private functions -- I can deal with that by the 
usual naming convention. Focus on the "separate namespace" aspect.

What I want is the ability to write this:

create Namespace:
    def function(x):
        return x

    def func(y):
        return function("spam") + "'n'" + y

    dispatch = {1: func, 2: function}

    def another_func(z):
        return len(dispatch[2](z))


func("eggs")  # raises NameError
Namespace.func("eggs")  # returns "spam'n'eggs"

Namespace.dispatch[1]("cheese")  # returns "spam'n'cheese"


etc. But of course we can't do that.

So other options include:

(1) Give up the dream and just place the private functions in the global 
namespace like everyone else does. So much for the Zen.

(2) Place them in a separate module, and import the module, similar to 
the way that the os module imports path. This is certainly possible, and 
it works well, but it splits code into two files when it should be in 
one. It pollutes the module namespace, and Guido has recently expressed 
mild objection to new code doing what os does. Or at least he suggested 
that we shouldn't *encourage* doing what os does.

(3) Redesign my module to use a full package structure, and place the 
functions in a separate module in the package. It's overkill to have a 
package for just two namespaces (modules).

(4) Use a class to hold the functions, instantiate the class to get an 
instance, and deal with the fact that functions in such an instance are 
automatically turned into methods. There are various ways of dealing with 
it, including the obvious: write the functions as methods.

But this is too Java-like for my liking... I don't need the ability to 
make multiple instances with independent state, nor do I want to waste 
time turning it into a singleton or borg class. Worse, it complicates the 
dispatch table, because I have to deal with bound methods versus unbound 
methods, or staticmethod/classmethod descriptors... it gets *messy* and 
*ugly* real fast.

(5) Use a class to hold the functions, but avoid instantiating it, so 
that the functions remain functions. I've done this, and it works great 
unless the functions need to refer to each other. So in the above 
example, Namespace.function("spam") is fine, but Namespace.func("eggs") 
is not.

Simple solution: Namespace.func could internally call Namespace.function 
instead of just function. That's doable now, without any magic, but it 
feels less than ideal.

(6) Do something with nested functions, although I'm not entirely sure 
what yet. See below.

(7) Create a module on the fly, then populate it with the functions:

Namespace = type(sys)('Namespace')

def function(x):
    return x

def func(y):
    return function("spam") + "'n'" + y

dispatch = {1: func, 2: function}

Namespace.function = function
Namespace.func = func
Namespace.dispatch = dispatch
del function, func, dispatch


(8) Something else.

There's always something else.


I've tried (1) through (5), with varying levels of success. (6) is next 
on my list to try. I haven't tried (7), and I don't think I will bother. 
It seriously breaks "Don't repeat yourself", and fails to visually set 
the Namespace functions apart from the global namespace. While I expect 
it will work, it's too ugly to consider.

So I have considered many options, and will look at others, but right now 
I've set myself the task of exploring (5) and finding out whether or not 
there is any way to hammer the class namespace into what I want. Duncan 
suggested a way, but it's messy and fragile.


TL;DR. See my first post :-P



-- 
Steven



More information about the Python-list mailing list