3

I have the following in Python 2.7:

class MyClass(object):
    ...
    @property
    def my_attr(self):
        ...

    @my_attr.setter
    def my_attr(self, value):
        ...

I use getter/setter so that I can do some logic in there.

Then I can call:

import myModule
test = myModule.MyClass()
test.my_attr = 9

I would like to use an alias at the module level so that I can do something like that:

import myModule
myModule.my_attr = 9

Is there a way to do that?

MasterMind
  • 379
  • 2
  • 17

2 Answers2

5

Yes, absolutely; the key is that modules are themselves objects. First you need to make MyClass subclass the module type:

from types import ModuleType

class MyClass(ModuleType):
    ...

Then you replace the current module with an instance of MyClass:

import sys
sys.modules[__name__] = MyClass(__name__)

Note that this can be pretty confusing to static analysers and to people reading your code.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • That seems to work. But what if I want to _append_ to the module instead of _replacing_? – MasterMind Aug 21 '13 at 19:39
  • @MasterMind something like `self.__dict__.update(real_module.__dict__)` should work. Or you can replace `self.__dict__` with a `collections.ChainMap` (since 3.3). – ecatmur Aug 21 '13 at 19:48
  • Note that you have to be careful when doing this -- see accepted answer to [this question](http://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name). – martineau Aug 21 '13 at 20:33
0

To provide a special handling for some attributes of a module, you could define a proxy class that does the special handling and delegates the rest to the original module object:

"""
>>> import property_on_module
>>> property_on_module.attr = 1
set attr property
>>> property_on_module.attr
get attr property
1
"""
import sys

class Module(object):
    def __init__(self, module):
        self.__module = module

    def __getattr__(self, name):
        return getattr(self.__module, name)

    @property
    def attr(self):
        print("get attr property")
        return self.__attr

    @attr.setter
    def attr(self, value):
        print("set attr property")
        self.__attr = value

if __name__ == "__main__": # test if run as a script
    import doctest
    sys.exit(doctest.testmod().failed)
else: # normal import, use `Module` class to provide `attr` property
    sys.modules[__name__] = Module(sys.modules[__name__])

__getattr__ might not be enough; you could define __getattribute__/__setattr__ in this case e.g., quickdraw.py (based on sh.py).

jfs
  • 399,953
  • 195
  • 994
  • 1,670