The question
Are there any valid use cases for the ol’ module switcheroo, where you replace the module with a class instance? By a valid use case, I mean a case where it would be generally agreed that using this trick would be the best way of solving a problem. For example, the module:
VERSION = (1, 2, 8)
VERSION_NAME = '1.2.8'
Could be converted to this:
import sys
class ConstantsModule(object):
def __init__(self):
self.VERSION = (1, 2, 8)
@property
def VERSION_NAME(self):
return u'{}.{}.{}'.format(*self.VERSION)
sys.modules[__name__] = ConstantsModule()
And now VERSION_NAME is a property with logic behind it.
I have googled around for this without finding anything relevant. I learned of this trick in a SO answer I read some time ago, and I know this is something referred to as “black magic” and to be avoided, but I’m curious about the valid use cases.
My specific use case
I have a small problem with one of my modules that could easily be solved if the module was a class instance. I have a “constant” called VERSION_NAME, which is a string version of VERSION, which in turn is a tuple with my application’s version information. The VERSION_NAME is used throughout my project and in several other projects based on this one. Now I would like VERSION_NAME to include some logic – I would like it to be based on VERSION so that I don’t have to edit it manually all the time, and I would like it to be formatted slightly differently depending on a couple of environmental circumstances. The way I see it I have two choices:
- Hunt down every use-case of
VERSION_NAMEin my project and all its sub-projects and change it to a function call likeget_version_name. - Invoke black magic like shown above.
This question is not about my use case though, this is just an example of what I figure it could be used for.
Since everything in Python is an object, there is no black magic about it; this is simply duck typing; if you create an object that walks and talks like a module, then the rest of Python is none the wiser.
However, for your specific use-case, you don’t need to resort to this level of deception. Simply calculate version name at import time:
Nowhere is it stated that module globals can only be literal values; just use a Python expression for them instead.
After all, your
VERSION_NAMEvariable is not going to change during the lifetime of your program, you only need to generate it once. Use a property only when you need an attribute that needs to be re-calculated every time you access it.