惰性模块变量——可以做到吗?

2024-04-28 04:34:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我正试图找到一种懒洋洋地加载模块级变量的方法。

具体来说,我已经编写了一个小的Python库来与iTunes对话,我希望有一个DOWNLOAD_FOLDER_PATH模块变量。不幸的是,iTunes不会告诉你它的下载文件夹在哪里,所以我编写了一个函数,它可以抓取一些podcast曲目的文件路径,并爬回目录树,直到找到“Downloads”目录。

这需要一两秒钟的时间,所以我希望延迟评估,而不是在模块导入时。

有没有什么方法可以在第一次访问模块变量时懒惰地分配它,或者我必须依赖一个函数?


Tags: 模块文件path方法函数路径目录文件夹
3条回答

我在Python3.3上使用了Alex的实现,但这崩溃得很惨: 守则

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

不正确,因为应该引发AttributeError,而不是KeyError。 这在Python 3.3下立即崩溃,因为已经做了很多内省工作 在导入过程中,查找诸如__path____loader__等属性

下面是我们现在在项目中使用的允许惰性导入的版本 在一个模块中。模块的__init__被延迟到第一个属性访问 没有特别的名字:

""" config.py """
# lazy initialization of this module to avoid circular import.
# the trick is to replace this module by an instance!
# modelled after a post from Alex Martelli :-)

Lazy module variables--can it be done?

class _Sneaky(object):
    def __init__(self, name):
        self.module = sys.modules[name]
        sys.modules[name] = self
        self.initializing = True

    def __getattr__(self, name):
        # call module.__init__ after import introspection is done
        if self.initializing and not name[:2] == '__' == name[-2:]:
            self.initializing = False
            __init__(self.module)
        return getattr(self.module, name)

_Sneaky(__name__)

模块现在需要定义一个init函数。此功能可以使用 要导入可能自行导入的模块,请执行以下操作:

def __init__(module):
    ...
    # do something that imports config.py again
    ...

代码可以放在另一个模块中,并且可以用属性进行扩展 就像上面的例子一样。

也许这对某人有用。

事实证明,从Python 3.7开始,可以通过在模块级定义一个__getattr__()来实现这一点,如PEP 562中所指定的。

# mymodule.py

from typing import Any

DOWNLOAD_FOLDER_PATH: str

def _download_folder_path() -> str:
    global DOWNLOAD_FOLDER_PATH
    DOWNLOAD_FOLDER_PATH = ... # compute however ...
    return DOWNLOAD_FOLDER_PATH

def __getattr__(name: str) -> Any:
    if name == "DOWNLOAD_FOLDER_PATH":
        return _download_folder_path()
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

你不能用模块来做,但是你可以把类伪装成一个模块,例如在itun.py,代码…:

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()

现在任何人都可以import itun。。。并获得您的itun._Sneaky()实例。这里的__getattr__允许您访问itun.py中的任何其他内容,这可能比在_Sneaky中编写顶级模块对象更方便!_)

相关问题 更多 >