如何检测是否在Python 3中导入了类/变量?

时间:2017-04-26 12:44:48

标签: python python-3.x import python-import

这是script_one.py

的内容
x = "Hello World"

这是script_two.py

的内容
from script_one import x
print(x)

现在,如果我运行script_two.py,输出将为:

>>> Hello World

我需要的是一种检测是否导入x的方法 这就是我想象的script_one.py的源代码:

x = "Hello World"
if x.has_been_imported:
  print("You've just imported \"x\"!")

然后,如果我运行script_two.py输出"应该"

>>> Hello World
>>> You've just imported "x"!

这叫做什么,Python 3中是否存在此功能以及如何使用它?

2 个答案:

答案 0 :(得分:2)

你不能。我试图发现这是浪费时间的努力,我很害怕。

Python导入包含以下步骤:

  • 查看sys.modules,检查模块是否已加载。
    • 如果尚未加载模块,请加载它。这将创建一个添加到sys.modules的新模块对象,其中包含执行顶级代码所产生的所有对象。
  • 在导入命名空间中绑定名称。如何绑定名称取决于所选的确切import变体。
    • import module将名称module绑定到sys.modules[module]对象
    • import module as othername将名称othername绑定到sys.modules[module]对象
    • from module import attribute将名称attribute绑定到sys.modules[module].attribute对象
    • from module import attribute as othername将名称othername绑定到sys.modules[module].attribute对象

在这种情况下,重要的是要认识到Python名称只是引用;所有Python对象(包括模块)都存在于堆中,并且随着对它们的引用数量的增加而停滞不前。如果您需要有关其工作原理的入门知识,请参阅此great article by Ned Batchelder on Python names

您的问题可以通过两种方式解释:

  • 您想知道模块已导入。执行模块中的时刻代码(如x = "Hello World"),它已被导入。所有的。 Python不会在这里加载 x,它全部或全部。
  • 您想知道其他代码是否使用特定名称。您必须跟踪对象存在的其他引用。这是一项庞大的任务,涉及递归检查gc.get_referrers() object chain以查看其他Python对象现在可能引用的x

在以下任何一种情况下,后一目标变得越来越难:

  • import script_one,然后使用script_one.x;像这样的引用可能太短暂,你无法检测到。
  • from script_one import x,然后del x。除非其他东西仍引用导入的命名空间中的相同字符串对象,否则该引用现在已经消失,无法再被检测到。
  • import sys; sys.modules['script_one'].x是引用相同字符串对象的合法方式,但这是否算作导入?
  • import script_one,然后list(vars(script_one).values())将创建模块中定义的所有对象的列表,但这些引用是列表中的索引,而不是命名。这是否算作进口?

答案 1 :(得分:0)

以前好像是不可能的。但是自从 python 3.7+ 在模块级别引入 __getattr__ 以来,现在看起来是可能的。至少我们可以区分一个变量是通过from module import varable还是import module; module.variable导入的。

思路是检测上一帧的AST节点,是否是Attribute

script_one.py


def _variables():
  # we have to define the variables 
  # so that it dosen't bypass __getattr__
  return {'x': 'Hello world!'}

def __getattr__(name):
  try:
    out = _variables()[name]
  except KeyError as kerr:
    raise ImportError(kerr)
  
  import ast, sys  
  from executing import Source
  
  frame = sys._getframe(1)
  node = Source.executing(frame).node
  if node is None:
    print('`x` is imported')
  else:
    print('`x` is accessed via `script_one.x`')

  return out

script_two.py

from script_one import x
print(x)
# `x` is imported
# 'Hello world!'

import script_one
print(script_one.x)
# `x` is accessed via `script_one.x`
# 'Hello world!'