Access descendant class's module namespace from superclass

Bengt Richter bokr at oz.net
Tue Jul 12 00:00:55 EDT 2005


On Mon, 11 Jul 2005 18:45:20 -0500, Reid Priedhorsky <reid at reidster.net> wrote:

>Dear group,
>
>I'd have a class defined in one module, which descends from another class
>defined in a different module. I'd like the superclass to be able to
>access objects defined in the first module (given an instance of the first
>class) without importing it. Example of what I'm looking for:
>
><<<file spam.py>>>
>
>   class Spam(object):
>      def fish(self):
>         a = self.__module__.Ham()
>
><<<file eggs.py>>>
>
>   import spam
>
>   class Eggs(spam.Spam):
>      pass
>
>   class Ham(object):
>      pass
>
>The above doesn't work because __module__ is a string, not a module object: 
>
>   >>> import eggs
>   >>> b = eggs.Eggs()
>   >>> b.fish()
>   Traceback (most recent call last):
>     File "<stdin>", line 1, in ?
>     File "spam.py", line 3, in foo
>       a = self.__module__.Ham()
>   AttributeError: 'str' object has no attribute 'Ham'
>
>(I suppose I could call __import__(self.__module__), but that seems kind
>of awkward.)
>
>Is this possible using Python 2.3? Any better ways to accomplish this?
>
>Thanks very much for any help,
>
>Reid

I think 2.3 will do this. But be careful not to tie your shoelaces together and trip --
I haven't tested this beyond what you see. It was just an idea for the kind of access
you seemed to want ;-)

If each class and subclass whose global environment you want to reach through an instance
provides a property  that will return the class or subclass' global dict, then a base class
method can access that via the instance to "fish" for a name in the appropriate "fishinghole"
-- e.g.,

----< spam.py >-------------------------------
"""spam.py module doc string"""
class Spam(object):
    def fish(self, whatfor):
        return self.fishinghole[whatfor]
    fishinghole = property(lambda self:globals()) # spam.py globals if this fishinghole used
----------------------------------------------

----< eggs.py >-------------------------------
"""eggs.py module doc string"""
import spam

class Eggs(spam.Spam):
    fishinghole = property(lambda self:globals()) # eggs.py globals if this fishinghole used

class Ham(object):
    pass    # won't find any fishing hole at all
    
class Grits(spam.Spam):
    pass    # no fishing hole, should find spam.py globals if looked for
----------------------------------------------

 >>> import eggs
 >>> dir(eggs)
 ['Eggs', 'Grits', 'Ham', '__builtins__', '__doc__', '__file__', '__name__', 'spam']
 >>> eggs.__doc__
 'eggs.py module doc string'
 >>>
 >>> e = eggs.Eggs()
 >>> e.fish('Ham')
 <class 'eggs.Ham'>
 >>> e.fish('__doc__')
 'eggs.py module doc string'
 >>> e.fishinghole.keys()
 ['Ham', 'spam', '__builtins__', '__file__', '__doc__', 'Grits', '__name__', 'Eggs']
 >>> g = eggs.Grits()
 >>> g.fish('Spam')
 <class 'spam.Spam'>
 >>> g.fish('__doc__')
 'spam.py module doc string'
 >>> g.fishinghole.keys()
 ['__builtins__', '__name__', '__file__', '__doc__', 'Spam']

You could fish for a class that might be available in both modules by the same name,
and get a different one depending on which instance' fish method or fishinghole you used.
Here Spam is only available in spam.py, but if it were available in eggs.py then e.fish('Spam')
would pick it up just like Ham.

 >>> spaminst = g.fish('Spam')()
 >>> spaminst.fish('__doc__')
 'spam.py module doc string'

Note that the fishinghole property dynamically returns the module dict,
which is mutable, so you can write a really tangled mess if you want to.
This already seems dangerously close ;-)

 >>> e.fishinghole['x'] = 'x in eggs module globals'
 >>> e.fish('x')
 'x in eggs module globals'
 >>> eggs.x
 'x in eggs module globals'
 >>> g.fishinghole['x'] = 'x in spam module globals'
 >>> g.fish('x')
 'x in spam module globals'
 >>> eggs.spam.x
 'x in spam module globals'

But we didn't directly import spam (eggs did, that's why eggs.spam was visible) ...
 >>> spam
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 NameError: name 'spam' is not defined
 >>> import spam
 >>> spam.x
 'x in spam module globals'

Regards,
Bengt Richter



More information about the Python-list mailing list