views:

118

answers:

3

I'm trying to find a way to lazily load a module-level variable.

Specifically, I've written a tiny Python library to talk to iTunes, and I want to have a DOWNLOAD_FOLDER_PATH module variable. Unfortunately, iTunes won't tell you where its download folder is, so I've written a function that grabs the filepath of a few podcast tracks and climbs back up the directory tree until it finds the "Downloads" directory.

This takes a second or two, so I'd like to have it evaluated lazily, rather than at module import time.

Is there any way to lazily assign a module variable when it's first accessed or will I have to rely on a function?

A: 

If that variable lived in a class rather than a module, then you could overload getattr, or better yet, populate it in init.

Sean Cavanagh
Yeah, I know. I wanted it in a module for the sake of the API.In a class, I'd use `property` to do it, though. Great for lazy loading.
wbg
+1  A: 

Is there any way to lazily assign a module variable when it's first accessed or will I have to rely on a function?

I think you are correct in saying that a function is the best solution to your problem here. I will give you a brief example to illustrate.

#myfile.py - an example module with some expensive module level code.

import os
# expensive operation to crawl up in directory structure

The expensive operation will be executed on import if it is at module level. There is not a way to stop this, short of lazily importing the entire module!!

#myfile2.py - a module with expensive code placed inside a function.

import os

def getdownloadsfolder(curdir=None):
    """a function that will search upward from the user's current directory
        to find the 'Downloads' folder."""
    # expensive operation now here.

You will be following best practice by using this method.

Simon Edwards
Hmmmm. It's the obvious and simplest way to do it, so in line with the Zen of Python, but I just don't like it API-wise.
wbg
+3  A: 

You can't do it with modules, but you can disguise a class "as if" it was a module, e.g., in itun.py, code...:

import sys

class _Sneaky(object):
  def __init__(self):
    self.download = None

  @property
  def DOWNLOAD_PATH(self):
    if not self.download:
      self.download = heavyComputations()
    return self.download

  def __getattr__(self, name):
    return globals()[name]

# other parts of itun that you WANT to code in
# module-ish ways

sys.modules[__name__] = _Sneaky()

Now anybody can import itun... and get in fact your itun._Sneaky() instance. The __getattr__ is there to let you access anything else in itun.py that may be more convenient for you to code as a top-level module object, than inside _Sneaky!_)

Alex Martelli
That is absolutely brilliant. A master at work.
wbg