如何添加可以被Python编译器绑定的全局符号?

时间:2016-04-18 15:14:15

标签: python compiler-errors global python-2.x symbols

这是我的第一个问题所以请你好:)我对Python很新,但我对其他编程语言(例如C ++)非常有经验。

更新2 - 找到解决方案

感谢大家的帮助:)由于解决方案在评论中“隐藏”,我将在此处重新发布。

而不是

file_symbols = {}

变量local_symbol最初必须添加到file_symbols字典中:

file_symbols = { "local_symbol" : local_symbol }

对于阅读此内容的任何人:此处发布的所有变量/类名称都不应被理解为实际有用的名称,因为这些示例本质上是合成的;)

嗯......现在我必须弄清楚:

的全部含义
exec compiled_code in file_symbols

到目前为止,我认为只使用file_symbols中找到的符号更新字典compiled_code。 但它实际上看起来更像一点! :)

更新1

好的,下面的示例项目似乎太简单了,无法显示实际问题。无论如何,感谢您已经提供的支持! :)

实际上我想首先编译需要访问本地符号(类实例)的多个* .py文件。应收集来自这些编译文件的所有符号,然后将其用作其他代码对象的环境。

所以我真的需要这样做 (注意以下代码显示的是概念,而不是实际的可执行代码):

class Functions:
    (...)

global_symbols = {}
local_symbol = Functions()

# note that the external files need to access local_symbol to call its functions!
for file in external_files:
    code = file.load()
    compiled_code = compile(code, "<string>", "exec")
    file_symbols = {}
    exec compiled_code in file_symbols
    global_symbols.update(file_symbols)

some_code = another_file.load()
compiled_code = compile(some_code, "<string>", "exec")
exec(compiled_code, global_symbols)

在这个例子中的行

exec compiled_code in file_symbols

生成一个NameError() - 因为它们无法访问local_symbol,因为它在外部文件中的任何地方都不是定义,尽管它应该被使用!

所以问题是如何为external_files提供local_symbol的访问权限,以便他们可以调用实例的函数?

我们有些人认为“hack”的导入钩子解决方案是目前唯一可行的解​​决方案。如果有的话,我会喜欢使用更简单的一个!

再次感谢:)

我最初的问题是:

所以我们走了。我打算做的是先进的东西,我在这里或其他任何地方找不到我的问题的解决方案。

在Python(2.6.x / 2.7.x)中假设以下代码:

class Functions:
    def __init__(self):
        (...)
    def func_1(...):
        (...)
    def func_2(...):
        (...)
    (...)
    def func_n(...):
        (...)
functions = Functions()
code = loadSomeFile(...)
compiled_code = compile(code, "<string>", "exec")
(...)
global_env = {}
local_env = {"func":functions}
exec(compiled_code, global_env, local_env)

上面示例中的code是从包含可能如下所示内容的文件加载的:

import something
(...)
def aFunction(...):
    a = func.func_1(...)
    b = func.func_2(...)
    return a * b
(...)
aFunction()

请注意,上面代码中的(...)表示为简单起见可能会遗漏更多代码。

我在我的示例中遇到的问题是编译器引发了此行的错误:

compiled_code = compile(code, "<string>", "exec")

我会收到此错误:NameError("global name 'func' is not defined")

此错误完全可以理解,因为编译器无法绑定到名称为“func”的任何全局符号。但我仍然希望以这种方式编译代码。

所以显而易见的问题是:

如何定义编译器可用于compile()语句的全局符号,以便编译器将任何“未知”符号绑定到我选择的对象上?

在我的示例中,我想定义绑定到func实例的全局符号class Functions,以便编译器在编译使用{{1}的代码时将找到此符号如上例所示。

那怎么能实现呢?

重要: 请注意,我知道使用func直接执行代码会修复编译问题,因为上例中的dict exec(...)将提供成功执行所需的符号。但是我不能这样做,因为要编译的代码根本不小。它可能包含数百个代码行,而且这段代码也只执行一次但很多次。 因此,出于性能原因,我真的需要编译代码而不是直接执行代码。

感谢您帮助我:)

4 个答案:

答案 0 :(得分:3)

请勿向globals单独提供localsexec个字词。这导致执行的代码表现得好像它嵌入在类定义中。这意味着exec代码绕过locals中定义的函数中的任何变量查找。

>>> exec("""
... def f():
...     print a
... f()""", {}, {"a": 3})
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<string>", line 4, in <module>
  File "<string>", line 3, in f
NameError: global name 'a' is not defined
>>> exec("""
... def f():
...     print a
... f()""", {"a": 3})
3

只需传递globals字典。

答案 1 :(得分:1)

简单的解释是您在本地命名空间中传入aFunctionaFunction无权访问您传入的本地人(它有自己的本地人)。所有func都可以访问自己的本地及其模块的全局变量。 assert globals() is locals()都不属于这些,因此函数调用失败。

大多数普通模块使用其全局变量和本地变量作为相同的命名空间(您可以自己查看dict)。这就是为什么您可以在模块级别定义事物并使它们可用于任何已定义的函数(模块中的所有名称都自动为全局)。

要完成这项工作,您需要使本地和全局变量相同func,或者根本不传递本地变量。如果你不想改变全局变量,那么只需复制全局变量然后将src = """ def aFunction(): a = func.func_1() b = func.func_2() return a + b value = aFunction() """ class Functions: def func_1(self): return "first" def func_2(self): return "second" functions = Functions() compiled_code = compile(src, "<string>", "exec") global_env = {} local_env = {"func":functions} namespace = dict(global_env) namespace.update(local_env) exec(compiled_code, namespace) print(namespace["value"]) 加到它上面。

{{1}}

答案 2 :(得分:0)

这是一个有趣的问题,感谢发布它。我一直在研究如何在编译时更改globals表。显然,您可以直接调用__import__()函数并执行:

  

传递你的全局变量以确定如何解释包上下文中的名称。

来源:Package documentation

答案 3 :(得分:0)

嗯,显然它可以通过简单地添加(并且可能删除 - 如果使用多个“func”实例)实例到sys.modules来实现:

sys.modules["func"] = functions
(...)
compiled_code = compile(code, "<string>", "exec")

完整且有效的解决方案包括导入器挂钩(截取源代码文件中的“import func”行)如下所示:

import sys

class Functions:
    def __init__(self):
        (...)
    def func_1(...):
        (...)
    def func_2(...):
        (...)
    (...)
    def func_n(...):
        (...)

functions = Functions()
code = loadSomeFile(...)

hook_name = "func"

class ImporterHook:
    def __init__(self, path):
        if path != hook_name:
            raise ImportError()
    def find_module(self, fullname, path=None):
        return self
    def load_module(self, path):
        if sys.modules.has_key(path):
            return sys.modules[path]
        else:
            sys.modules[path] = functions
            return functions

sys.path_hooks.append(ImporterHook)
sys.path.insert(0, hook_name)
compiled_code = compile(code, "<string>", "exec")
(...)
exec(compiled_code)

看起来并不那么难:)有关详细信息,请参阅此处:

https://www.python.org/dev/peps/pep-0302/#specification-part-2-registering-hooks

在这里:

https://pymotw.com/2/sys/imports.html

谢谢:)