【问题标题】:Importing a shared library and the global namespace导入共享库和全局命名空间
【发布时间】:2012-01-11 01:05:05
【问题描述】:

我正在尝试导入一个共享库,其中包含许多用于可视化程序的 Python 包装器(具体来说是VisIt)。这个库的实现方式是,首先导入库,这使得一些函数可用,然后调用一个函数来启动可视化查看器并使其余 API 可供调用。比如下面的

form visit import *
print dir()
Launch()
print dir()

第一个打印语句包含常用的内置函数和一些其他函数

['AddArgument', 'GetDebugLevel', 'Launch', 'SetDebugLevel', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__warningregistry__']

第二次打印输出

['ActivateDatabase', 'AddArgument', 'AddColorTable', 'AddOperator', 'AddPlot', ... ]

等等

我希望改为在函数中调用Launch(这样我就可以处理并将参数传递给Launch)。但是,当我这样做时,在调用 Launch 后可用的函数不在全局命名空间中,而是在函数本地的命名空间中。所以在下面的例子中

import sys
from visit import *

def main():

    Launch()
    print dir()
    if "Version" in dir()
        print Version() # This is made available by call to Launch() above

    return 0

if __name__=="__main__":
    ret = main()
    print dir()
    sys.exit(ret)

main 中的 print 语句将打印出来

['ActivateDatabase', 'AddArgument', 'AddColorTable', 'AddOperator', 'AddPlot', ... ]

如上,而在main 之后的print 被称为 prints

['AddArgument', 'GetDebugLevel', 'Launch', 'SetDebugLevel', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__warningregistry__']

好像Launch 从未被调用过。

我的第一个问题是如何确保通过调用 Launch 填充全局命名空间?

其次,对Version 的调用实际上失败了,并出现错误

NameError:未定义全局名称“版本”

即使print "Version" in dir() 返回True。如果我解决了我的第一个问题,这个问题是否会得到解决,或者这完全是另外一回事?

如果您需要共享库上方的更多信息,请告诉我。我不太了解它是如何编写的,但我可以尝试找出答案。

编辑:按照@voithos 的回答,以下是我采用的解决方案。

正如@voithos 所说,“访问使用动态导入将所有内容都带到本地范围......假设您永远不会在全局范围之外调用visit.Launch()。”他的(初始)回答允许我使用前缀 visit. 与所有这些例程一起使用 visit.Launch() 提供的功能,以便在我的主例程之外(和之内)使用。

要将 VisIt 例程导入为from visit import *,以便可以在没有visit. 前缀的情况下调用它们,我将@voithos 在main 中对setattr 的使用修改为以下

# Loop through the local namespace and add the names that were just
# imported to the module namespace
loc = locals()
for key in loc:
    setattr(sys.modules[__name__], key, loc[key])

那么 VisIt 例程在模块级别可用,一切看起来都很好。

感谢@voithos 的回答。

【问题讨论】:

  • 我最初将我的编辑作为答案发布,但觉得最好将其作为我的问题的更新。

标签: python shared-libraries


【解决方案1】:

似乎 Visit 使用动态导入,将所有内容都带到本地范围。基本上,他们假设您永远不会在全局范围之外调用visit.Launch()。启动函数都是用 C++ 编写的,所以我不确定它是如何导入的。

一种解决方法是将名称从本地范围重新分配到另一个范围。 (例如,导入您导入的 visit 模块)这是一个示例:

import sys
import visit

def main():
    visit.Launch()

    # Loop through the locals that were just imported
    # and assign the names to the visit module
    loc = locals()
    for key in loc:
        setattr(visit, key, loc[key])

    return 0

if __name__=="__main__":
    ret = main()
    print dir(visit)
    sys.exit(ret)

如果您想将名称分配给全局范围,则可以将循环修改为:

loc = locals()
glob = globals()

for key in loc:
    glob[key] = loc[key]

但请注意:这样做会覆盖您之前定义的任何冲突符号名称。例如,如果您定义了一个Version() 函数,则访问模块中的Version() 将覆盖您定义的旧函数。因此,除非您确实需要,否则不要在全局范围内加上名称,这通常是一个好主意。

【讨论】:

  • 感谢@voithos 的回答 - 这很好用,例如,现在可以在mainmain 之后成功调用版本。但是,我可以使用类似的解决方法将 main 的本地名称添加到全局命名空间。如果我想在 VisIt 和标准 Python 解释器中使用相同的脚本,这一点很重要。
  • @Chris:当然,这很容易。只需分配给全局模块而不是visit。您可以使用globals() 访问全局符号表。我已修改答案以显示示例。
【解决方案2】:

我从未使用过 VisIt,但根据他们的文档,您需要这样做:

import visit

def main():    
    visit.Launch()

这会将所有函数导入“访问”命名空间。然后,您只需通过访问命名空间与库的函数进行交互。如:

visit.Version()

【讨论】:

  • 这给了我同样的问题:如果我使用import visit 并在用main 调用print dir(visit) 之后替换打印语句,那么我没有得到完整的API,只有函数在调用 Launch 之前最初可用。
  • 我应该说我使用from visit import *而不是import visit是有原因的。我的问题是visit.Launch 提供的功能在main 之外无法访问。
猜你喜欢
  • 2014-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-19
  • 2017-12-20
  • 1970-01-01
  • 2013-05-20
  • 1970-01-01
相关资源
最近更新 更多