【问题标题】:Set variable to the name of the module that has imported it将变量设置为已导入它的模块的名称
【发布时间】:2015-08-03 18:58:57
【问题描述】:

假设我有两个模块:

first.package.module

# Assign module to a variable
which_module = ???

print("I originally live in {}".format(__name__))   # Prints first.package.module
print("I was run from {}".format(which_module))

second.package.module

from first.package.module import *

third.package.module

from first.package.module import *

如何在 second.package.module 中导入后打印第二行“I was run from”以打印 second.package.module?或者 third.package.module 从那里运行时。

我想要这种行为的原因是由于在 Django 中构建了一个在同一个项目中多次使用的应用程序,即同一个应用程序有多个实例。这些实例中的每一个都有自己的模型,这些模型继承自抽象模型。为了构建可重用的视图和 url,我想动态加载模型,为此,我需要知道哪个模块导入了应用程序并运行它。

【问题讨论】:

  • 你为什么需要它?您要解决什么问题需要您查找帧堆栈以查看首先导入的内容?
  • @MartijnPieters:我试图在帖子末尾解释它。我有一个 Django 应用程序,我在同一个项目中多次使用它。应用程序的每个实例都有自己的模型。所以,要编写一个可重用的 url 和视图版本,我需要知道正在处理哪个实例,这样我才能动态加载相应的模型。
  • 为什么在创建 url 和模型的时候不直接传入一个参数呢?或者使用 Django 配置变量?您的代码在导入时仅运行一次,因此如果您在同一个 Django 进程中执行此操作,您甚至无法执行您想要的操作。
  • 我试过了,但想不出一个好的方法来解决它。我想可以用变量来包装所有东西(例如,urlpatterns),但我也失去了星型导入所有东西的简单性。

标签: python django python-3.x django-1.7


【解决方案1】:

您可以打印触发模块加载的模块名称:

import sys

print sys._getframe(1).f_globals['__name__']

在带有新的importlib 堆栈的 Python 3 中,您需要将帧数增加到 6(至少对于 3.5、3.6 和 3.7):

print(sys._getframe(6).f_globals['__name__'])

跨 CPython 的解决方案是过滤掉堆栈中的任何 importlib._bootstrap* 元素:

import sys

def imported_from(depth=0):
    # skip the frames of this function, and the caller
    f = sys._getframe(2 + depth)
    while f and f.f_code.co_filename.startswith("<frozen importlib._bootstrap"):
        f = f.f_back
    return f and f.f_globals['__name__']

print(imported_from())

上面的imported_from()函数可以在实用模块中定义并导入; sys._getframe(2) 调用确保起始上下文是触发imported_from() 调用的任何内容。提供一个正整数作为depth 参数以增加跳过的帧数。该函数适用于任何提供 sys._getframe() 函数的 Python 版本,无论它是否使用 importlib 堆栈。

请注意:

  • 依赖于 CPython 实现细节(暴露帧堆栈在其他 Python 实现中不可用)。见sys._getframe() function documentation

    CPython 实现细节:此函数应仅用于内部和专用目的。不保证在 Python 的所有实现中都存在。

  • 在 Python 3 中,它取决于 importlib 堆栈的确切实现细节;正在进行的开发可以在该堆栈中添加或删除调用。例如,在首次引入importlib 的 Python 3.3 中,要跳过的正确堆栈计数是 9,在 3.4 中它下降到 7,而 3.5 将它下降到 6(这个数字后来一直很稳定)。 imported_from() 解决了这个问题,但引入了一个新的依赖于实现细节的假设:可以通过在文件名中查找 &lt;frozen importlib._bootstrap 前缀来检测与导入有关的堆栈帧。理论上,可以使用未冻结的importlib._bootstrap 编译CPython(不作为marshal 数据数组包含在解释器二进制文件中)。

  • 仅在第一次导入模块时有效,此时 Python 会执行模块代码以生成 sys.modules 对象。

最后一点我怎么强调都不为过。 Python 只加载一个模块一次。导入是一个两步过程:加载和绑定。加载步骤仅在模块对象不存在时执行,模块的所有其他导入仅绑定名称

绑定名称实际上是无法挂钩的;一旦模块已经在内存中,import modulename 只不过是一个 modulename = sys.modules['modulename'] 赋值。

【讨论】:

  • 感谢您的回答。然而,这只给了我 importlib._bootstrap.
  • @ChristofferKarlsson:在 Python 3 中,您必须增加帧数。
  • @ChristofferKarlsson:正如我已经说过的:你不能。这不是 Python 导入的工作方式。 Python 只加载一次代码,然后从加载的对象中绑定名称。它不会为每个导入语句重新执行您的代码。
  • @M.I.Wright:它完全依赖于当前版本的实现细节,所以没有。但它是 Python 3.5、3.6 和 3.7 的正确数字。
  • @M.I.Wright:我更新了答案以强调这一点。
猜你喜欢
  • 2012-11-15
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多