3

Can I create a module that uses functions that will be supplied by importers of the module?

I want to write generic code without specifying the implementation of a few key functions. I want the user to specify these. I could include these as inputs to all functions but this seems ugly.

If I were writing an object I would create an abstract class. Is there an equivalent for modules?

Edit: I'm asking specifically about how to do this with functions in modules, not methods in classes.

MRocklin
  • 55,641
  • 23
  • 163
  • 235
  • 1
    Something like http://stackoverflow.com/questions/3928023/is-it-possible-to-overload-from-import-in-python perhaps? – Silas Ray Sep 13 '12 at 13:09
  • When you say you want the user to specify the functions, do you want them to do so once and use those for the duration of the program, or to dynamically use different functions depending upon the context from which your dynamic module is called from? – Silas Ray Sep 13 '12 at 13:27
  • Why would you specifically want to do this with functions in modules? It seems that for your use case it is much easier to use classes instead of modules. – Hans Then Sep 13 '12 at 14:14

2 Answers2

2

It is possible to monkey patch a module and add new functions to a module at runtime.

import my_module
def my_implementation():
    pass

my_module.implementation = my_implementation

However this is frowned upon in the Python community. It is inherently unstable as this will affect all users of this module. For instance, if you use another module which also uses the module that you have monkey patched, your other module may show erratic behavior that is hard to debug.

For what you want to do you'd better create an abstract class :-)

Hans Then
  • 10,935
  • 3
  • 32
  • 51
  • Just a regular class with a few missing methods. – Hans Then Sep 13 '12 at 13:15
  • 1
    @Tichodroma, as Hans says, you can create a regular class with a few missing methods (or better, a few methods that raise `NotImplementedError`). However, there is also the ABC (Abstract Base Class), or you can build your own abstract class system with metaclasses. – Silas Ray Sep 13 '12 at 13:17
  • See http://docs.python.org/library/abc.html for abstract bases classes. However, abstract base classes are really overkill for what you want to do. See the rationale for abstract bases classes here: http://www.python.org/dev/peps/pep-3119/ – Hans Then Sep 13 '12 at 13:32
  • 1
    I think monkey patch is simply the perfect answer when you want to know what would happen (in terms of memory and time efficiency) if you change some data-type implementation. You can write the new implementation in a "hidden" module, and "plug" it in to compare timings and memory usage. – Bakuriu Sep 13 '12 at 13:39
  • monkeypatching works, but it's not dynamic or predictable, and it's definitely not automatic... I wonder if you could build a factory that would create proxies to a given module, then stick that in sys.modules instead of the module object itself, so you could dynamically and automatically pull functions in to the module proxy instance referenced in the importing namespace, on import... – Silas Ray Sep 13 '12 at 13:46
  • Isn't that a bit overkill? When reading the post, I wonder "why would you want to do that", when it is much easier to redesign your module to use classes. – Hans Then Sep 13 '12 at 14:13
1

If you do it in a module, you will have a problem if someday in a same program one module want to use your generic module with an implementation, and another module want to use it with another.

The solution is to write a module that mainly contains a class that provide the wanted services.

Look in the "random" module for example. All the module functions like "randint", "uniform", "gauss" are actually bound to method of an hidden instance of random.Random class. But you can also use your own instance of Random class, or subclass it by redefining a few key methods to change the way random is generated but keep all the useful methods.

See http://docs.python.org/library/random.html

MatthieuW
  • 2,292
  • 15
  • 25