25

事情是这样的,我有一个代理持有对远程模块的引用,我将其中的一些代理放在sys.modules这样我可以像本地模块一样使用它的地方。但是其他一些对象被放在__builtin__远程环境的模块中(例如为了方便调试或引用的魔术变量)。我不想像这样引用这些变量conn.__builtin__.var,并且我必须替换本地__builtin__(这似乎不适用于替换sys.modules['__builtin__']或挂钩全局名称查找规则。如何?对于一个模块,您可以重载 agetattr来执行此操作。但是在像这样的交互式解释器中IPython,谁是主要模块或如何做到这一点? 更新:正如@Nizam Mohamed 所指出的,是的,我可以得到__main__模块,但我仍然无法修改它的名称查找角色。

我想将本地环境完全变成远程环境(用于调试控制台)

更新

现在我只是迭代所有的__builtin__.__dict__,如果有一个名字不在 local 中__builtin__。我将名称添加到本地的__builtin__. 但与名称查找规则相比,它并不是那么动态,如果我在本地找不到名称,请__builtin__尝试远程名称。

是一个类似的讨论。

这个问题通过将其替换为sys.modules. 但这不适用于__builtin__名称查找,我还尝试将其替换__builtin__.__getattribute__为自定义名称,该自定义名称将首先使用原始查找,然后在失败时使用自定义查找。__builtin__但是从不调用的全局名称查找__builtin__.__getattribute__甚至__builtin__.__getattribute__('name')返回所需的值,__builtin__.name或者name从不返回一个。

4

2 回答 2

7

使用 IPython shell 的 AST 转换

正如@asmeurer 所说,您可以编写一个简单的 AST 转换器来“挂钩”变量名查找。基类ast.NodeTransformer提供了一个您可以操作的visit_Name方法。你只需要重载这个方法来重新定义那些存在于远程模块中但不在本地的变量。

以下模块可用作IPython 扩展

测试AST.py

import ast

modName = "undefined"
modAttr = []
user_ns = {}
class MyTransformer(ast.NodeTransformer):
    def visit_Name(self, node):
        if node.id in modAttr and not node.id in user_ns: 
          return self.getName(node)
        return node
    def getName(self, NameNode):
        return ast.Attribute(value=ast.Name(id=modName, ctx=ast.Load()), 
                             attr = NameNode.id, 
                             ctx  = NameNode.ctx)
def magic_import(self, line):
    global modName, modAttr, user_ns
    modName = str(line)
    if not self.shell.run_code( compile('import {0}'.format(line), '<string>', 'exec') ):
       user_ns = self.shell.user_ns
       modAttr = user_ns[line.strip()].__dict__
       self.shell.ast_transformers.append(MyTransformer())
       print modName, 'imported'
    
def load_ipython_extension(ip):
    ip.define_magic('magic_import', magic_import)

虚拟模块.py

robot=" World"

用法:

In [1]: %load_ext testAST
In [2]: %magic_import dummyModule
In [3]: print "Hello" , robot
Hello World

In [4]: dummyModule.robot_II = "Human" 
In [5]: print "Hi", robot_II
Hi Human

这种方法的好处是对远程模块的任何修改都会立即生效,因为查找是在语言级别完成的,并且没有对象被复制和缓存。

这种方法的一个缺点是不能处理动态查找。如果这对您很重要,那么python_line_transforms钩子可能更合适。

于 2016-05-12T19:08:09.683 回答
6

有一种方法可以获取模块将使用的所有名称的列表。虽然它没有修改查找机制,但我相信它可以解决您的问题。

这是一些可以放入模块中的代码(希望可以理解),我们称之为magic

import sys

def magic():
    # Get the caller frame
    frame = sys._getframe().f_back

    # Unwind all internal Python import-related stuff
    while frame.f_code.co_filename.startswith('<'):
        frame = frame.f_back

    importer = frame

    # Iterate through names the module has/will use
    for name in importer.f_code.co_names:

        # If the module has not yet defined/imported this name
        if name not in importer.f_globals and \
                name not in importer.f_locals and \
                name not in __builtins__:

            # Replace the name in the importer's namespace
            # You'll have to replace the right-hand side by your specific code
            importer.f_globals[name] = 'hello world'

然后你可以导入它来做魔术:

import magic
magic.magic()

print(hi)

然而,有两个缺点。首先,动态查找将失败:

import magic
magic.magic()

print(globals()['hi']) # KeyError: 'hi'

尽管可以通过查看 中的字符串来解决该特殊情况importer.f_code.co_consts,但它不适用于更高级的动态查找,例如print(globals()['h'+'i'])

第二个缺点是它在函数中不起作用:

import magic
magic.magic()

def f():
    print(hi) # NameError: name 'hi' is not defined

f()

这是因为在这种情况下,名称hi是 inf.__code__.co_names 而不是模块的co_names.

一种可能的解决方案是修改magic以尝试查找模块的所有函数并更改它们的代码,但它不适用于函数内部定义的函数等。

另一种解决方案是调用magic函数:

import magic

def f():
    magic.magic()
    print(hi)

f()
于 2016-05-06T21:35:25.847 回答